C#学习之UI线程和线程池线程之间通讯之TaskScheduler.FromCurrentSynchronizationContext()

讲了如何在UI线程和线程池线程之间通讯。一般来说,UI线程拥有的对象,其他线程是无法操作的。但是.Net有一个很重要的抽象对象——TaskScheduler(任务调度器)。它协调着不同任务(线程)的运行,使得线程池中的线程有了操作UI线程的可能。在我以前不知道OberservableCollection和WPF开发的时候,都是要么使用 Invoke方法,要么就是使用TaskScheduler。这篇文章我们就主要介绍一下TaskScheduler这个对象。
本篇的例子依然来源于《C#多线程编程实例》(其实我这个系列的都是来自于这本书的例子)
建立wpf应用,编写如下函数:
[csharp] view plain copy 在CODE上查看代码片派生到我的代码片
Task<string> TaskMethod(TaskScheduler scheduler)  
{  
Task delay = Task.Delay(5000);  
  
return delay.ContinueWith(t =>  
{  
string str = string.Format("Task is running on a thread id {0}. Is thread pool thread: {1}",  
Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);  
ContentTextBlock.Text = str;  
return str;  
}, scheduler);  
}  


这个TaskMethod方法有一个TaskScheduler参数,方法内,先Delay 5秒钟,类似于sleep。然后在delay任务完成后做后续操作,返回当前任务的相关信息到str字符串,并且将ContentTextBlock.Text属性修改为str字符串。需要注意的是,根据不同scheduler,该后续操作将在不同的线程上运行(可能是线程池线程,也可能是UI线程),但是请注意线程池线程是无法完成上述UI线程操作的(因为ContentTextBlock是UI线程中的对象)。


首先来看默认情况下的TaskScheduler:
[csharp] view plain copy 在CODE上查看代码片派生到我的代码片
void ButtonSync_Click(object sender, RoutedEventArgs e)  
{  
ContentTextBlock.Text = string.Empty;  
try  
{  
string result = TaskMethod(TaskScheduler.Default).Result;  
ContentTextBlock.Text = result;  
}  
catch (Exception ex)  
{  
ContentTextBlock.Text = ex.InnerException.Message;  
}  
}  
就像我之前所说的那样,该段函数是无法正常运行的。因为TaskScheduler.Default表明是在线程池中开线程运行,由于不是UI线程,因此会报错。




然后,我们来看下第二个例子,注意我加的注释!照样出错,同上图,因此就不截图了。
[csharp] view plain copy 在CODE上查看代码片派生到我的代码片
void ButtonAsync_Click(object sender, RoutedEventArgs e)  
{  
ContentTextBlock.Text = string.Empty;  
Mouse.OverrideCursor = Cursors.Wait;  
Task<string> task = TaskMethod(TaskScheduler.Default);//这个函数主要就是这里用了Default,所以用了线程池线程,而TaskMethod里面有操作UI对象,因此出错!  
task.ContinueWith(t => {  
ContentTextBlock.Text = t.Exception.InnerException.Message;//这里的后续操作是在UI线程中做的,没有出错。  
Mouse.OverrideCursor = null;  
},   
CancellationToken.None,  
TaskContinuationOptions.OnlyOnFaulted,  
TaskScheduler.FromCurrentSynchronizationContext());  
}  


最后一个例子是能够正确运行的,因为使用了TaskScheduler.FromCurrentSynchronizationContext(),因此函数中task会运行在一个与当前函数相同的线程中(也就是UI线程中),所以在TaskMethod中对于UI对象的更改可以被正确处理。同样,后续操作也是在UI线程中进行。
[csharp] view plain copy 在CODE上查看代码片派生到我的代码片
void ButtonAsyncOK_Click(object sender, RoutedEventArgs e)  
{  
ContentTextBlock.Text = string.Empty;  
Mouse.OverrideCursor = Cursors.Wait;  
Task<string> task = TaskMethod(TaskScheduler.FromCurrentSynchronizationContext()); // task 并没有运行在线程池中,而是 FromCurrentSynchronizationContext  
task.ContinueWith(t => Mouse.OverrideCursor = null,  
CancellationToken.None,  
TaskContinuationOptions.None,  
TaskScheduler.FromCurrentSynchronizationContext());  
}  

补充一点,这里的UI线程的thread id 也是1。


最后来补充一个经典的死锁案例,将上例中的代码增加一行Result:
string s = task.Result; //这句话将让UI线程等待直到UI线程完成task中的内容,但是等待中的UI线程没有办法操作,因此死锁!
[csharp] view plain copy 在CODE上查看代码片派生到我的代码片
void ButtonAsyncOK_Click(object sender, RoutedEventArgs e)  
{  
MessageBox.Show(Thread.CurrentThread.ManagedThreadId.ToString());  
ContentTextBlock.Text = string.Empty;  
Mouse.OverrideCursor = Cursors.Wait;  
Task<string> task = TaskMethod(TaskScheduler.FromCurrentSynchronizationContext());   
string s = task.Result; //这句话将让UI线程等待直到UI线程完成task中的内容,但是等待中的UI线程没有办法操作,因此死锁!  
task.ContinueWith(t => Mouse.OverrideCursor = null,  
CancellationToken.None,  
TaskContinuationOptions.None,  
TaskScheduler.FromCurrentSynchronizationContext());  

}  


转自http://blog.csdn.net/huiwuhuiwu/article/details/53581730

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值