假设我尝试使用的API定义为(我将省略事件定义):
public sealed class DodgyService{ public voID MethodThatHasToBeCalledOnTheMainThread() { ... }}
为了使用这个API,我在我的库中添加了一个名为Service(Yup,非常原始名称)的服务,它将创建一个新任务(当我指定一个从SynchronizationContext创建的TaskScheduler时,它将在主线程上运行).
这是我的实施:
public class Service{ private Readonly TaskFactory _taskFactory; private Readonly TaskScheduler _mainThreadScheduler; public Service(TaskFactory taskFactory,TaskScheduler mainThreadScheduler) { _taskFactory = taskFactory; _mainThreadScheduler = mainThreadScheduler; } // Assume this method can be called from any thread. // In this sample is called by the main thread but most of the time // the caller will be running on a background thread. public Task ExecuteAsync(string taskname) { return _taskFactory.StartNew( () => ReallyLongCallThatForWhateverStupIDReasonHasToBeCalledOnMainThread(taskname),new CancellationToken(false),TaskCreationoptions.None,_mainThreadScheduler) .ContinueWith(task => Trace.Traceinformation("ExecuteAsync has completed on \"{0}\"...",taskname)); } private voID ReallyLongCallThatForWhateverStupIDReasonHasToBeCalledOnMainThread(string taskname) { Trace.Traceinformation("Starting \"{0}\" really long call...",taskname); new DodgyService().MethodThatHasToBeCalledOnTheMainThread(); Trace.Traceinformation("Finished \"{0}\" really long call...",taskname); }
}
现在,如果我执行我的服务调用(在主线程上)并尝试在主线程上等待应用程序进入死锁,因为主线程将等待已安排在主线程上执行的任务.
如何在不阻塞整个过程的情况下将这些调用编组到主线程上?
在某些时候,我想在创建新任务之前执行主线程的检测,但我不想破解它.
对于任何感兴趣的人,我得到了一个带有代码的虚拟here和一个展示该问题的WPF应用程序.
在btw上,库必须写在.net framework 4.0上
编辑!
我按照0700提供的建议按照here提供的方式解决了我的问题
private voID HandleClosed(object sender,EventArgs e) { var List = new[] { _service.ExecuteAsync("first task"),_service.ExecuteAsync("second task"),_service.ExecuteAsync("third task") }; //uncommenting this line blocks all three prevIoUs activitIEs as expected //as it drives the current main thread to wait for other tasks waiting to be executed by the main thread. //Task.WaitAll(List); }
Task.WaitAll是一个阻塞调用,你不能在主线程上执行阻塞调用,否则你将导致死锁.你可以做什么(如果你使用的是Visual Studio 2012或更新版本)是使用NuGet软件包Microsoft.Bcl.Async
,它为.Net 4.0提供异步/等待支持.
添加包后,将代码更改为
private async voID HandleClosed(object sender,EventArgs e){ var List = new[] { _service.ExecuteAsync("first task"),_service.ExecuteAsync("third task") }; //uncommenting this line blocks all three prevIoUs activitIEs as expected //as it drives the current main thread to wait for other tasks waiting to be executed by the main thread. await TaskEx.WhenAll(List);}
并且你的程序将不再死锁(在等待TaskEx.WhenAll(List)之后它也不会执行任何代码;但这是因为此代码在关闭过程中运行,当你等待它时,让关闭继续处理,如果它被放置在其他地方,就像点击事件,你会看到更多的正常行为).
另一种选择是拥有第二个“主线程”并将工作分配给它.通常当必须在“主”线程上运行某些东西实际上是说它们需要在“一个STA的windows消息上运行,该对象最初是在”线程上创建的.这是一个如何实现的例子(取自here)
private voID runbrowserThread(Uri url) { var th = new Thread(() => { var br = new Webbrowser(); br.documentCompleted += browser_documentCompleted; br.Navigate(url); Application.Run(); }); th.SetApartmentState(ApartmentState.STA); th.Start();}voID browser_documentCompleted(object sender,WebbrowserdocumentCompletedEventArgs e) { var br = sender as Webbrowser; if (br.Url == e.Url) { Console.Writeline("Natigated to {0}",e.Url); Application.ExitThread(); // Stops the thread }}总结
以上是内存溢出为你收集整理的如何使用C#中的TPL任务将工作编组到主线程上而不会导致死锁?全部内容,希望文章能够帮你解决如何使用C#中的TPL任务将工作编组到主线程上而不会导致死锁?所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)