MFa*_*MAR 71 c# lambda task task-parallel-library
我正在开展一个多任务网络项目,我是新手Threading.Tasks.我实现了一个简单的Task.Factory.StartNew(),我想知道我该怎么做Task.Run()?
这是基本代码:
Task.Factory.StartNew(new Action<object>(
(x) =>
{
// Do something with 'x'
}), rawData);
Run Code Online (Sandbox Code Playgroud)
我System.Threading.Tasks.Task在对象浏览器中查看,我找不到Action<T>类似的参数.只有Action这需要void参数,没有类型.
只有两件事情similiar:static Task Run(Action action)和static Task Run(Func<Task> function),但不能发布与两个参数(一个或多个).
是的,我知道我可以,但创建一个简单的扩展方法,我的主要问题是,我们可以把它写在一行用Task.Run()?
Zer*_*er0 93
private void RunAsync()
{
string param = "Hi";
Task.Run(() => MethodWithParameter(param));
}
private void MethodWithParameter(string param)
{
//Do stuff
}
Run Code Online (Sandbox Code Playgroud)
编辑
由于受欢迎的需求,我必须注意,Task启动将与调用线程并行运行.假设默认情况下TaskScheduler这将使用.NET ThreadPool.无论如何,这意味着您需要考虑传递给Task多个线程可能同时访问的任何参数,从而使它们成为共享状态.这包括在调用线程上访问它们.
在上面的代码中,案例完全没有实际意义.字符串是不可变的.这就是我用它们作为例子的原因.但是说你没有使用String......
一种解决方案是使用async和await.默认情况下,这将捕获SynchronizationContext调用线程,并在调用await并将其附加到创建的方法后为方法的其余部分创建延续Task.如果此方法在WinForms GUI线程上运行,则它将是类型WindowsFormsSynchronizationContext.
继续将在回发到捕获后运行SynchronizationContext- 仅在默认情况下再次运行.因此,您将在await电话会议后回到您开始使用的主题.您可以通过各种方式更改此功能,尤其是使用ConfigureAwait.总之,该方法的其余部分将不会继续,直到之后的Task已完成了对另一个线程.但是调用线程将继续并行运行,而不是方法的其余部分.
等待完成运行该方法的其余部分可能是也可能不是所希望的.如果后来该方法中没有任何内容访问传递给Task您的参数,则可能根本不想使用await.
或者您可能在稍后的方法中使用这些参数.没有理由await立即继续安全地工作.请记住,您可以将Task返回的变量存储在变量中,await稍后再使用相同的方法.例如,一旦你做了一些其他的工作后,你需要安全地访问传递的参数.同样,你也没有需要await上Task,当你运行它的权利.
无论如何,关于传递给参数的这个线程安全的简单方法Task.Run是这样做:
你必须先装饰RunAsync有async:
private async void RunAsync()
Run Code Online (Sandbox Code Playgroud)
重要的提示
优选地,标记的方法不应返回无效,如链接文档所述.常见的例外是事件处理程序,例如按钮点击等.他们必须归还无效.否则我总是尝试返回或使用时.出于好几个原因,这是一种很好的做法.async TaskTask<TResult>async
现在你可以await在Task下面运行.你不能await没有async.
await Task.Run(() => MethodWithParameter(param));
//Code here and below in the same method will not run until AFTER the above task has completed in one fashion or another
Run Code Online (Sandbox Code Playgroud)
因此,通常情况下,如果您await执行任务,则可以避免将传入的参数视为可能的共享资源,同时具有从多个线程修改某些内容的所有缺陷.另外,要注意关闭.我不会深入介绍那些,但链接的文章做得很好.
边注
有点偏离主题,但要小心使用WinForms GUI线程上的任何类型的"阻塞",因为它被标记[STAThread].使用await不会阻塞,但有时我会看到它与某种阻塞一起使用.
"Block"在引号中,因为您在技术上无法阻止WinForms GUI线程.是的,如果你使用lock上的WinForms GUI线程它会仍然抽取消息,尽管你认为它的"封杀".不是.
在非常罕见的情况下,这可能会导致奇怪的问题.lock例如,你永远不想使用绘画的原因之一.但这是一个边缘而复杂的案例; 但是我看到它会导致疯狂的问题.所以我完整地注意到它.
Sco*_*ain 28
使用变量捕获来"传入"参数.
var x = rawData;
Task.Run(() =>
{
// Do something with 'x'
});
Run Code Online (Sandbox Code Playgroud)
您也可以rawData直接使用,但必须小心,如果更改rawData任务外部的值(例如for循环中的迭代器),它也会更改任务内部的值.
Arn*_* F. 12
从现在开始,您还可以:
Action<int> action = (o) => Thread.Sleep(o);
int param = 10;
await new TaskFactory().StartNew(action, param)
Run Code Online (Sandbox Code Playgroud)
只需使用 Task.Run
var task = Task.Run(() =>
{
//this will already share scope with rawData, no need to use a placeholder
});
Run Code Online (Sandbox Code Playgroud)
或者,如果您想在方法中使用它并稍后等待任务
public Task<T> SomethingAsync<T>()
{
var task = Task.Run(() =>
{
//presumably do something which takes a few ms here
//this will share scope with any passed parameters in the method
return default(T);
});
return task;
}
Run Code Online (Sandbox Code Playgroud)
我知道这是一个旧线程,但是我想分享一个最终不得不使用的解决方案,因为接受的帖子仍然有问题。
问题:
正如Alexandre Severino指出的那样,如果param(在下面的函数中)在函数调用后不久发生更改,您可能会在中出现一些意外行为MethodWithParameter。
Task.Run(() => MethodWithParameter(param));
Run Code Online (Sandbox Code Playgroud)
我的解决方案:
为了解决这个问题,我最终编写了一些类似于以下代码的代码:
(new Func<T, Task>(async (p) => await Task.Run(() => MethodWithParam(p)))).Invoke(param);
Run Code Online (Sandbox Code Playgroud)
这使我能够安全地异步使用参数,尽管事实上在启动任务后参数变化很快(这导致发布的解决方案出现问题)。
使用这种方法,param(值类型)会传入其值,因此,即使async方法在param更改后运行,它p也将具有运行param此行代码时的值。
| 归档时间: |
|
| 查看次数: |
105315 次 |
| 最近记录: |