开发者

how to get a task completion notification in non gui thread

Background: I used to call a stored procedure during my Form Load. However, since this resulted in a suboptimal UI experience, I put my SP call in a task of its own inside the Shown event. Since this is typically the last event in the form display process, it resulted in a much better experience than putting stuff in the Form load event. I have:

private void MainForm_Shown(object sender, EventArgs e)
{
   dbCheckTask = Task<bool>.Factory.StartNew(RunSPAndCheckStuff());

  // both of below run on the GUI thread.

  // if I print the thread ID in mycallback it is the GUI thread id
  dbCheckTask.ContinueWith(开发者_开发知识库mycallback());  

  // I also tried below. But obviously, that too runs on the GUI thread
  mycallback(dbCheckTask.Result)

}

Because they fire on the GUI thread, my startup form paint is still neither instantaneous nor smooth. How can I get my task complete callback on a non-GUI thread without resorting to events? Whenever the task completes and if something is wrong and only if something is wrong (bool result returned false) then the user gets a message box pop. Until then he could go ahead and do other non database related stuff on the form. Please advise how I can get a task completion callback with task result in a non gui thread. Thank you


All this stuff is addressed best in the Async language extensions you can download here and has the homepage here.

It introduces the async and await keywords to C# and VB that will let you write code that switches back and forth between UI and background threads effortlessly even within a single method. The compiler will convert that to tasks, continuations, error catching etc etc transparantly without you having to worry about any of that. The example that would interest you would be this one:

    public async void AsyncSwitchToCPU()    {
    Console.WriteLine("On the UI thread.");

    // Switch to a thread pool thread:
    await new SynchronizationContext().SwitchTo();  

    Console.WriteLine("Starting CPU-intensive work on background thread...");
    int result = DoCpuIntensiveWork();
    Console.WriteLine("Done with CPU-intensive work!");

    // Switch back to UI thread
    await Application.Current.Dispatcher.SwitchTo();                

    Console.WriteLine("Back on the UI thread.  Result is {0}.", result);
}

public int DoCpuIntensiveWork()
{
    // Simulate some CPU-bound work on the background thread:
    Thread.Sleep(5000);
    return 123;
}

This even has a go-live license (with some reservations from MS). Very elegant stuff borrowed from F#.

Rgds Gert-Jan


I'd use a BackgroundWorker for this, personally. One way to get your callback to run on the task thread would be to modify your method call and task creation as follows:

private void MainForm_Shown(object sender, EventArgs e)
{
    dbCheckTask = Task<bool>.Factory.StartNew(() => RunSPAndCheckStuff(mycallback));
    ...
}

private bool RunSPAndCheckStuff(Action<bool> callback)
{
    bool result = false;
    // Do stuff
    callback(result);
    return result;
}


You should look into using the Asynchronous API's rather than calling the synchronous versions in a background thread:

http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.beginexecutenonquery.aspx

The advantage to that is that no thread will be blocked, and I believe the callback will be called on ThreadPool thread, e.g. NOT on the GUI thread. From there you can marshal any GUI calls back to the GUI thread with Invoke/BeginInvoke.


Why not doing:

Task.Factory.StartNew(()=>WorkerMethod());

And define WorkerMethod() as:

void WorkerMethod()
{
     RunSPAndCheckStuff(); // this blocks until finished
     DoSomeMoreStuff(); // then this continuous
}

Otherwise please provide more details on what do you want to accomplish.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜