use*_*870 0 .net c# task winforms task-parallel-library
我有接收文件列表的功能,并在单个/单独的线程中工作:
public void DoWork(params...)
{
    _filesInProcess = 0;
    _filesFinished = 0;
    _tokenSource = new CancellationTokenSource();
    var token = _tokenSource.Token;
    Task.Factory.StartNew(() =>
    {
        try
        {
            Parallel.ForEach(_indexedSource,
                new ParallelOptions
                {
                    MaxDegreeOfParallelism = parallelThreads //limit number of parallel threads 
                },
                file =>
                {
                    if (token.IsCancellationRequested)
                        return;
                    //do work...
                });
        }
        catch (Exception)
        { }
    }, _tokenSource.Token).ContinueWith(
            t =>
            {
                //finish...
                 if (OnFinishWorkEventHandler != null)
                     OnFinishWorkEventHandler(this, EventArgs.Empty);
            }
        , TaskScheduler.FromCurrentSynchronizationContext() //to ContinueWith (update UI) from UI thread
        );
}
我添加了在循环中运行此命令的选项,在此函数完成后从主窗体运行,如果我需要另一个循环,我再次调用此函数,但在第二次冻结我的所有UI,我无法找到原因
这就是我调用我的函数的方式:
private void StartJob()
{
    string[] files = GetlFiles().ToArray();
    Job job = new Job(files);
    timerStatus.Enabled = true;
    job.OnStartPlayEventHandler += job_OnStartPlayEventHandler;
    job.OnFinishPlayEventHandler += job_OnFinishPlayEventHandler;
    job.OnFinishWorkEventHandler += job_OnFinishWorkEventHandler;
    job.StartTimerEventHandler += job_StartTimerEventHandler;
    job.StopTimerEventHandler += job_StopTimerEventHandler;
    job.DoWork(
                NetworkAdapter.SelectedAdapter.PacketDevice,
                ReadSpeed(),
                PlayOption.Regular,
                ChecksumFixer.FixBadChecksum,
                ReadParallelThreads(PlayState.Single),
                Iteration.Loops
            );
}
完成此操作后:
private void job_OnFinishWorkEventHandler(object sender, EventArgs e)
{
    Job job = sender as Job;
    job.OnStartPlayEventHandler -= job_OnStartPlayEventHandler;
    job.OnFinishPlayEventHandler -= job_OnFinishPlayEventHandler;
    job.OnFinishWorkEventHandler -= job_OnFinishWorkEventHandler;
    job.StartTimerEventHandler -= job_StartTimerEventHandler;
    job.StopTimerEventHandler -= job_StopTimerEventHandler;
    Iteration.LoopFinished++;
    if (Iteration.LoopFinished < Iteration.Loops) // In case i want another loop
        StartJob();
    else
    {
        UnlockButtonsAfterPlay();
        UnlockContextMenuAfterPlay();
    }
}
而正如我提到的所有卡在第二次迭代
Task.Factory.StartNew很危险.它使用TaskScheduler.Current而不是TaskScheduler.Default.
在你的情况下OnFinishWorkEventHandler被触发从ContinueWithUI线程中运行TaskScheduler.FromCurrentSynchronizationContext().所以那时TaskScheduler.Current是UIScheduler而不是TaskScheduler.Default(threadpool scheduler).这就是为什么你的第二个任务在UI线程中安排,导致UI冻结.
解决这个Task.Run总是指向的用法TaskScheduler.Default.Task.Run是.net 4.5中的新功能,如果你在.net 4.0中,你可以创建你TaskFactory的默认参数,你可以使用它.
private static readonly TaskFactory factory = new TaskFactory(CancellationToken.None,
        TaskCreationOptions.None, TaskContinuationOptions.None, TaskScheduler.Default);
然后用
factory.StartNew(...);
如果您只是在自己不需要创建工厂实例时才使用它,那么只需TaskScheduler明确指定即可.
Task.Factory.StartNew(() =>{
   //Your code here
}, _tokenSource.Token,
   TaskCreationOptions.None,
   TaskScheduler.Default)//Note TaskScheduler.Default here
.ContinueWith(
        t =>
        {
            //finish...
             if (OnFinishWorkEventHandler != null)
                 OnFinishWorkEventHandler(this, EventArgs.Empty);
        }
    , TaskScheduler.FromCurrentSynchronizationContext());
| 归档时间: | 
 | 
| 查看次数: | 524 次 | 
| 最近记录: |