2

Thanks for your time and assistance in advance.

I am using TPL from WPF application to download files from a remote server. I have a 'transfer manager' method that runs on the UI thread, from which I create background Tasks to download files individually. I attach continuation tasks on the background tasks to update UI.

Problem is: for first file, the download tasks is starting on a new thread as expected. But for all subsequent downloads, it is using the same thread as UI locking up the application.

I have done my homework, read relevant material on this and hence I am unable to understand this behaviour, determine what I am doing wrong and how to fix it. Any help, tips, pointers, explanation is appreciated. Thanks.

I am attaching sample code and output window text from debug mode.

private void btnNew_Click(object sender, RoutedEventArgs e)
{
// do some work and prepare transfer state object(my custom object)
...
..
  iTaskFactory = new TaskFactory
          (TaskCreationOptions.LongRunning | TaskCreationOptions.AttachedToParent,
           TaskContinuationOptions.None);

// call transfer manager
this.ManageTransfer(null, iDownloadState);
}

private void ManageTransfer(Task antecedent, TransferState ts)
{
  if(antecedent == null)
  // this is the first invocation of the task from GetNew / Upload methods
  {
    ts.IsActive = true;
    // some code....
  }
  else if(antecedent.IsFaulted)       // check exception on previous task executed
  // if there is a fault, then retry the page again
  {

    // do some error logging....
    //retry transferring the same page again
    pageToTranfer = ts.PagesTransferred + 1;
  }
  else
  // if the page was successfully transferred
  {
    //increment transfer count
    ts.PagesTransferred += 1;

    // update UI.... (as running on UI Thread)

    // if all pages are transferred, then exit method. No further tasks to queue
    if(ts.PagesTransferred == ts.Pages)
    {          
      return;
    }

    pageToTranfer = ts.PagesTransferred + 1;
  }

  Task transferTask;     // should run in background thread
                         // **Problem is that afer first execution**, 
                         // **this also runs on UI Thread!!**
  Task continuationTask; // should run in UI thread

    localFile = "someName.txt";  //..... work out next file to transfer
    serverFile = "someName.txt"; //..... work out next file to transfer

  time = DateTime.Now.ToString("hh:mm:ss.ffff"); //.ToShortTimeString();    
  Debug.WriteLine(time + "starting download :" + pageToTranfer.ToString() 
                  + " This is Thread: " 
                  + Thread.CurrentThread.ManagedThreadId.ToString());

  transferTask = iTaskFactory.StartNew(() => Download(serverFile, localFile));

  continuationTask = transferTask.ContinueWith((t1Task) => ManageTransfer(t1Task, ts)
                     , TaskScheduler.FromCurrentSynchronizationContext());

}

public bool Download(string aCloudPath, string aLocalPath)
{

  time = DateTime.Now.ToString("hh:mm:ss.ffff"); //.ToShortTimeString();
  Debug.WriteLine(time + " in background for downloading " 
                 + aCloudPath + " using thread:" 
                 + Thread.CurrentThread.ManagedThreadId.ToString());

  // code to transfer file... no access to UI or any shared variable

  return true;
}

Output Window:

10:37:53.2629 starting download :1 This is Thread: 8

10:37:55.2720 in background for downloading file-01.txt using thread:15

10:37:56.4120 starting download :2 This is Thread: 8

10:38:00.4143 in background for downloading file-02.txt using thread:8

10:38:01.2413 starting download :3 This is Thread: 8

10:38:05.2445 in background for downloading file-03.txt using thread:8

10:38:05.8606 starting download :4 This is Thread: 8

10:38:09.8618 in background for downloading file-04.txt using thread:8

As you can see, first time the download function ran in background as expected. But after that it keeps on running in UI Thread!! I am expecting ManagerTranfer to run in the same thread and a different Thread to be used for Download function.

I have tried this with simple Tasks (i.e. without TaskFactory) as well, but the behaviour is same. I have set TaskCreationOptions.LongRunning, so TPL should start a new thread regardless!!!

svick
  • 236,525
  • 50
  • 385
  • 514
  • Fixed. I removed my custom TaskFactory and set TaskScheduler.Default on my download task, when starting. – Snehal Tiwari Oct 08 '12 at 06:58
  • The scheduler.Default is usually the threadpool, but if you are inside another task that was started using FromCurrentSynchronizationContext, then this default scheduler is the UI thread. – Tormod Oct 08 '12 at 09:10
  • 1
    @Tormod I believe you're wrong. `TaskScheduler.Default` is always the thread pool, `TaskScheduler.Current` changes the way you describe. – svick Oct 08 '12 at 09:45

1 Answers1

1

The scheduler that you are using is that which runs the tasks in currentSync context, that will in turn run your tasks on the UI thread alongside other tasks that are scheduled on the same scheduler. In order to run your tasks on the seperate thread, you should use another scheduler which implements the taskscheduler class and makes thread accordingly.

A scheduler similar is used in this post, but then again you will have to do a certain level of customization to your scheduler as well, as I can see you are using currentthread as well, which might be not that easy when implementing your own scheduler. On the other hand you can always use some helpful ParallelTaskExtensions and QueuedtaskScheduler provides some useful implementations that can be used in this regard.

Community
  • 1
  • 1
faizanjehangir
  • 2,771
  • 6
  • 45
  • 83