从.NET 4.0开始,有TPL来执行异步任务。如果您正在阅读msdn,则与窗体/ UI交互的所有异步操作仍使用InvokeRequire ... Invoke()模式。 我问的是,有什么理由吗?根据我的经验,TPL应该是旧的线程机制的替代品。那么当关于UI线程时忽略它有什么意义呢? 有什么想法吗?
答案 0 :(得分:4)
这似乎相当主观......
当你说“自.NET 4.0以来”,你说的是“截至今年4月” - .net已经存在了10年了,InvokeRequired / Invoke已经被用于最后的9.为什么要MS出于任何原因打破所有现有的UI代码?即使存在一种新的调用线程的方式,他们也不能简单地修改模式而没有很大的兼容性问题。
此外,TPL与InvokeRequired / Invoke不相似--TPL与简单并行有关,而invoke则是关于在特定线程上运行代码。即使没有兼容性问题,我也不确定为什么会替换另一个。
请注意,没有任何停止您使用TPL来确保在正确的线程上调用UI组件。事实上,你可以轻松地做到这一点。但这取决于您,当前的API不会以不向后兼容的方式发生变化。
答案 1 :(得分:1)
使用TPL,您可以使用TaskScheduler.FromCurrentSynchronizationContext指定目标线程,此方法指定将在主线程上执行任务。我建议使用它而不是Invoke。
答案 2 :(得分:1)
这里有什么问题? TPL的存在并没有改变UI本身就是单线程的事实,并且只需要在UI线程上访问控件。 (这是Windows限制,而不是.NET UI框架的限制.TPL无法改变Windows设计的数十年限制。)
如果你的问题是关于将Tasks与InvokeRequired / Invoke混合,那么有一种比Invoke更面向TPL的方式。 TPL提供了一种内置方式来安排在UI线程上运行的继续任务。因此,您将面向后台的工作放在一个任务中,然后在另一个任务中更新UI。请参阅this post on task schedulers and SynchronizationContext。
(但实际上,TPL不会替换任何旧的线程API。如果Invoke是您尝试做的最佳方式,请使用它。)
答案 3 :(得分:0)
private void WorkProcessingAsync(IWorkItem workItem)
{
IsBusy = true;
/* =============================
* Create a TPL Task and pass the current UiCulture in an state Object to resolve the correct .resx file for translation / globalisation / Multilanguate features in Background Thread
* ==============================*/
Task<IWorkItem> task = Task.Factory.StartNew((stateObj) =>
{
// here we are already in the task background thread
// save cast the given stateObj
var tuple = stateObj as Tuple<IWorkItem, CultureInfo>;
Debug.Assert(tuple != null, "tuple != null");
Thread.CurrentThread.CurrentUICulture = tuple.Item2; // Here we set the UI-Thread Culture to the Background Thread
var longRunningOperationAnswer = LongRunningOperation.DoLongWork(tuple.Item1);
return longRunningOperationAnswer;
}, new Tuple<IWorkItem, CultureInfo>(workItem, Thread.CurrentThread.CurrentUICulture)); // here we pass the UI-Thread Culture to the State Object
/* =======================================================================
* Handle OnlyOnRanToCompletion Task and process longRunningOperationAnswer back in UiThread
* =======================================================================*/
task.ContinueWith((t) =>
{
IsBusy = false;
// handle longRunningOperationAnswer here in t.Result
Log.Debug("Operation completet with {0}", t.Result);
}, CancellationToken.None
, TaskContinuationOptions.OnlyOnRanToCompletion
, TaskScheduler.FromCurrentSynchronizationContext());
/* =======================================================================
* Handle OnlyOnFaulted Task back in UiThread
* =======================================================================*/
task.ContinueWith((t) =>
{
IsBusy = false;
AggregateException aggEx = t.Exception;
if (aggEx != null)
{
aggEx.Flatten();
Log.ErrorFormat("The Task exited with Exception(s) \n{0}", aggEx);
foreach (Exception ex in aggEx.InnerExceptions)
{
if (ex is SpecialExaption)
{
//Handle Ex here
return;
}
if (ex is CustomExeption)
{
//Handle Ex here
return;
}
}
}
}, CancellationToken.None
, TaskContinuationOptions.OnlyOnFaulted
, TaskScheduler.FromCurrentSynchronizationContext());
}