C# 任务。是否使用参数运行?
我正在从事一个多任务网络项目,我对C# 任务。是否使用参数运行?,c#,lambda,task-parallel-library,task,C#,Lambda,Task Parallel Library,Task,我正在从事一个多任务网络项目,我对Threading.Tasks是新手。我实现了一个简单的Task.Factory.StartNew(),我想知道如何使用Task.Run() 以下是基本代码: Task.Factory.StartNew(new Action<object>( (x) => { // Do something with 'x' }), rawData); Task.Factory.StartNew(新操作( (x) => { //用“x”做点什么 })
Threading.Tasks
是新手。我实现了一个简单的Task.Factory.StartNew()
,我想知道如何使用Task.Run()
以下是基本代码:
Task.Factory.StartNew(new Action<object>(
(x) =>
{
// Do something with 'x'
}), rawData);
Task.Factory.StartNew(新操作(
(x) =>
{
//用“x”做点什么
}),原始数据);
我在对象浏览器中查看了System.Threading.Tasks.Task
,找不到类似Action
的参数。只有Action
接受void
参数,没有类型
只有两件事是相似的:静态任务运行(动作动作)
和静态任务运行(函数)
,但不能同时发布参数
是的,我知道我可以为它创建一个简单的扩展方法,但是我的主要问题是我们可以用Task.Run()
在单行上编写它吗
编辑
由于流行的需求,我必须注意启动的任务将与调用线程并行运行。假设默认的TaskScheduler
,这将使用.NET线程池。无论如何,这意味着您需要考虑传递给任务的任何参数可能同时被多个线程访问,使它们处于共享状态。这包括在调用线程上访问它们
在我上面的代码中,这种情况完全没有意义。字符串是不可变的。这就是为什么我以他们为例。但是如果你没有使用字符串
一种解决方案是使用async
和wait
。默认情况下,这将捕获调用线程的SynchronizationContext
,并在调用wait
后为方法的其余部分创建一个延续,并将其附加到创建的任务上。如果此方法在WinForms GUI线程上运行,则其类型将为WindowsFormsSynchronizationContext
继续将在回发到捕获的同步上下文
后运行-仅在默认情况下。因此,在wait
调用之后,您将回到开始的线程。您可以通过多种方式改变这一点,特别是使用。简而言之,在另一个线程上完成任务之前,该方法的其余部分将不会继续。但是调用线程将继续并行运行,而不是方法的其余部分
等待完成运行方法的其余部分可能是可取的,也可能是不可取的。如果以后该方法中没有任何内容访问传递给任务的参数
,则您可能根本不想使用wait
或者,您以后可能会在方法中使用这些参数。没有理由立即等待
,因为您可以继续安全地工作。请记住,您可以将返回的任务
存储在一个变量中,并在以后对其进行等待
——即使使用相同的方法。例如,在完成一系列其他工作后,需要安全地访问传递的参数。同样,在运行任务时,您不需要等待任务
无论如何,对于传递给Task.Run
的参数,使此线程安全的一种简单方法是:
您必须首先使用async
装饰RunAsync
:
private async void RunAsync()
重要提示
如链接文档所述,标记为的方法最好不返回void。常见的例外是事件处理程序,如按钮单击等。它们必须无效地返回。否则,在使用async
时,我总是尝试返回任务
或任务
。这是一个很好的实践,有很多原因
现在,您可以等待
运行任务
,如下所示。如果没有async
,则不能使用wait
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
因此,一般来说,如果您等待任务,您可以避免将传入的参数视为潜在的共享资源,从而避免同时从多个线程修改某些内容的所有陷阱。另外,要小心。我不会深入讨论这些内容,但链接文章做得很好
旁注
有点离题,但在WinForms GUI线程上使用任何类型的“阻塞”都要小心,因为它被标记为[STAThread]
。使用await
根本不会阻塞,但有时我确实看到它与某种阻塞结合使用
“Block”在引号中,因为你在技术上。是的,如果在WinForms GUI线程上使用lock
,它仍然会输出消息,尽管您认为它被“阻止”。不是
在极少数情况下,这可能会导致奇怪的问题。例如,在绘制时,您永远不想使用锁的原因之一。但这是一个边缘和复杂的情况;然而,我看到它引起了疯狂的问题。因此,为了完整起见,我注意到了它。使用变量捕获来“传入”参数
var x = rawData;
Task.Run(() =>
{
// Do something with 'x'
});
您也可以直接使用rawData
,但必须小心,如果在任务外部更改rawData
的值(例如for
循环中的迭代器),它也会更改任务内部的值。只需使用任务。运行
var task = Task.Run(() =>
{
//this will already share scope with rawData, no need to use a placeholder
});
或者,如果您希望在方法中使用它,并等待稍后的任务
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;
}
公共任务somethingsync()
{
var task=task.Run(()=>
{
//大概做一些需要几毫秒的事情
//这将与方法中传递的任何参数共享作用域
返回默认值(T);
});
返回任务;
}
从现在起,您还可以:
Action<int> action = (o) => Thread.Sleep(o);
int param = 10;
await new TaskFactory().StartNew(action, param)
Action动作=(o)=>Thread.Sleep(o);
int参数=10;
等待新的TaskFactory().StartNew(操作,参数)
我知道这是一个旧线程,但我想分享一个最终不得不使用的解决方案
Action<int> action = (o) => Thread.Sleep(o);
int param = 10;
await new TaskFactory().StartNew(action, param)
Task.Run(() => MethodWithParameter(param));
(new Func<T, Task>(async (p) => await Task.Run(() => MethodWithParam(p)))).Invoke(param);
for (int i = 0; i < 300; i++)
{
Task.Run(() => {
var x = ComputeStuff(datavector, i); // value of i was incorrect
var y = ComputeMoreStuff(x);
// ...
});
}
for (int ii = 0; ii < 300; ii++)
{
System.Threading.CountdownEvent handoff = new System.Threading.CountdownEvent(1);
Task.Run(() => {
int i = ii;
handoff.Signal();
var x = ComputeStuff(datavector, i);
var y = ComputeMoreStuff(x);
// ...
});
handoff.Wait();
}
struct Job { public int P1; public int P2; }
…
for (int i = 0; i < 100; i++) {
var job = new Job { P1 = i, P2 = i * i}; // structs immutable...
Task.Run(() => DoSomething(job));
}