Question

I have function that received List of files and do work in single/separate threads:

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
        );
}

I added the option to run this command in loops, from the main form after this function finished and if i need another loop i call to this function another time but in the second time this freeze all my UI and i cannot find out why

This is how i call to my function:

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
            );
}

After this operation finished:

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();
    }
}

And as i mention all stuck in second iteration

Was it helpful?

Solution

Task.Factory.StartNew is dangerous. It uses TaskScheduler.Current as opposed to TaskScheduler.Default.

In your case OnFinishWorkEventHandler is fired from ContinueWith which is running in UI thread by TaskScheduler.FromCurrentSynchronizationContext(). So at that point TaskScheduler.Current is UIScheduler and not TaskScheduler.Default(threadpool scheduler). That's why your second task is scheduled in UI Thread which causes UI to freeze.

To fix this use Task.Run which always points to TaskScheduler.Default. Task.Run is new in .net 4.5, if you're in .net 4.0 you can create your TaskFactory with default parameters and you can use that.

private static readonly TaskFactory factory = new TaskFactory(CancellationToken.None,
        TaskCreationOptions.None, TaskContinuationOptions.None, TaskScheduler.Default);

Then use

factory.StartNew(...);

If you're just going to use it only once you don't need to create a factory instance yourself, just specifying TaskScheduler explicitly will be fine.

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());
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top