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));
}