Task.Run 带参数? [英] Task.Run with Parameter(s)?

查看:17
本文介绍了Task.Run 带参数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在处理一个多任务网络项目,我是 Threading.Tasks 的新手.我实现了一个简单的 Task.Factory.StartNew(),我想知道如何使用 Task.Run() 来实现?

I'm working on a multi-tasking network project and I'm new on Threading.Tasks. I implemented a simple Task.Factory.StartNew() and I wonder how can I do it with Task.Run()?

基本代码如下:

Task.Factory.StartNew(new Action<object>(
(x) =>
{
    // Do something with 'x'
}), rawData);

我在对象浏览器中查看了System.Threading.Tasks.Task,但找不到类似Action的参数.只有 Action 接受 void 参数,没有 type.

I looked into System.Threading.Tasks.Task in Object Browser and I couldn't find a Action<T> like parameter. There is only Action that takes void parameter and no type.

只有两件事相似:static Task Run(Action action)static Task Run(Func function) 但不能发布参数) 两者.

There are only 2 things similiar: static Task Run(Action action) and static Task Run(Func<Task> function) but can't post parameter(s) with both.

是的,我知道我可以为它创建一个简单的扩展方法,但我的主要问题是我们可以用 Task.Run() 将它写在一行上吗?

Yes, I know I can create a simple extension method for it but my main question is can we write it on single line with Task.Run()?

推荐答案

private void RunAsync()
{
    //Beware of closures.  String is immutable.
    string param = "Hi";
    Task.Run(() => MethodWithParameter(param));
}

private void MethodWithParameter(string param)
{
    //Do stuff
}

编辑

由于普遍需求,我必须注意启动的 Task 将与调用线程并行运行.假设默认 TaskScheduler 这将使用 .NET ThreadPool.无论如何,这意味着您需要将传递给 Task 的任何参数考虑在内,因为它们可能会同时被多个线程访问,从而使它们共享状态.这包括在调用线程上访问它们.

Due to popular demand I must note that the Task launched will run in parallel with the calling thread. Assuming the default TaskScheduler this will use the .NET ThreadPool. Anyways, this means you need to account for whatever parameter(s) being passed to the Task as potentially being accessed by multiple threads at once, making them shared state. This includes accessing them on the calling thread.

在我上面的代码中,这种情况完全没有实际意义.字符串是不可变的.这就是为什么我用它们作为例子.但是假设您没有使用 String...

In my above code that case is made entirely moot. Strings are immutable. That's why I used them as an example. But say you're not using a String...

一种解决方案是使用asyncawait.默认情况下,这将捕获调用线程的 SynchronizationContext,并在调用 await 后为方法的其余部分创建一个延续,并将其附加到创建的 <代码>任务.如果此方法在 WinForms GUI 线程上运行,它将是 WindowsFormsSynchronizationContext 类型.

One solution is to use async and await. This, by default, will capture the SynchronizationContext of the calling thread and will create a continuation for the rest of the method after the call to await and attach it to the created Task. If this method is running on the WinForms GUI thread it will be of type WindowsFormsSynchronizationContext.

continuation 将在回发到捕获的 SynchronizationContext 后运行 - 同样仅在默认情况下.因此,您将在 await 调用后返回到您开始的线程上.您可以通过多种方式更改此设置,特别是使用 ConfigureAwait.简而言之,该方法的其余部分将不会继续,直到 之后 Task 在另一个线程上完成.但是调用线程将继续并行运行,只是方法的其余部分不会.

The continuation will run after being posted back to the captured SynchronizationContext - again only by default. So you'll be back on the thread you started with after the await call. You can change this in a variety of ways, notably using ConfigureAwait. In short, the rest of that method will not continue until after the Task has completed on another thread. But the calling thread will continue to run in parallel, just not the rest of the method.

等待完成其余方法的运行可能是可取的,也可能是不可取的.如果之后该方法中没有任何内容访问传递给 Task 的参数,您可能根本不想使用 await.

This waiting to complete running the rest of the method may or may not be desirable. If nothing in that method later accesses the parameters passed to the Task you may not want to use await at all.

或者您可能会在稍后的方法中使用这些参数.没有理由立即等待,因为您可以继续安全地工作.请记住,您可以将返回的 Task 存储在一个变量中,然后在其上存储 await - 即使使用相同的方法.例如,一旦您需要在完成一些其他工作后安全地访问传递的参数.同样,当您运行 Task 时,您不需要await.

Or maybe you use those parameters much later on in the method. No reason to await immediately as you could continue safely doing work. Remember, you can store the Task returned in a variable and await on it later - even in the same method. For instance, once you need to access the passed parameters safely after doing a bunch some other work. Again, you do not need to await on the Task right when you run it.

无论如何,使这个线程安全的一个简单的方法是:

Anyways, a simple way to make this thread-safe with respect to the parameters passed to Task.Run is to do this:

你必须先用 async 修饰 RunAsync:

You must first decorate RunAsync with async:

private async void RunAsync()

重要说明

最好是标记为async<的方法/code> 不应返回无效,如链接文档所述.对此的常见例外是事件处理程序,例如按钮单击等.他们必须返回无效.否则,我总是尝试在使用 async 时返回 TaskTask.出于多种原因,这是一种很好的做法.

Preferably the method marked async should not return void, as the linked documentation mentions. The common exception to this is event handlers such as button clicks and such. They must return void. Otherwise I always try to return a Task or Task<TResult> when using async. It's good practice for a quite a few reasons.

现在您可以await运行Task,如下所示.如果没有 async,你就不能使用 await.

Now you can await running the Task like below. You cannot use await without 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

因此,一般来说,如果您await 任务,您可以避免将传入的参数视为潜在的共享资源,从而避免同时从多个线程修改某些内容的所有陷阱.另外,请注意闭包.我不会深入介绍这些内容,但链接的文章做得很好.

So, in general, if you await the task you can avoid treating passed in parameters as a potentially shared resource with all the pitfalls of modifying something from multiple threads at once. Also, beware of closures. I won't cover those in depth but the linked article does a great job of it.

关于 RunStartNew 下面的代码我觉得最重要的是要知道,真的.使用任何一个都有正当理由,既不是过时的,也不是更好的"比另一个.请注意,除非您理解这一点,否则简单地将一个替换为另一个是一个非常糟糕的主意:

Regarding Run and StartNew the code below I find most important to know, really. There are legitimate reasons to use either, neither is obsolete or "better" than the other. Be aware simply replacing one with the other is a very bad idea unless you understand this:

//These are exactly the same
Task.Run(x); 
Task.Factory.StartNew(x, CancellationToken.None,
TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);

//These are also exactly the same
Task.Factory.StartNew(x);
Task.Factory.StartNew(x, CancellationToken.None, 
TaskCreationOptions.None, TaskScheduler.Current);

附注

有点离题,但要小心使用任何类型的阻塞"在 WinForms GUI 线程上,因为它被标记为 [STAThread].使用 await 根本不会阻塞,但我有时会看到它与某种阻塞结合使用.

A bit off topic, but be careful using any type of "blocking" on the WinForms GUI thread due to it being marked with [STAThread]. Using await won't block at all, but I do sometimes see it used in conjunction with some sort of blocking.

阻止"用引号引起来是因为您在技术上无法阻止 WinForms GUI 线程.是的,如果您在 WinForms GUI 线程上使用 lock,它仍然会发送消息,尽管您认为它被阻止"了.不是.

"Block" is in quotes because you technically cannot block the WinForms GUI thread. Yes, if you use lock on the WinForms GUI thread it will still pump messages, despite you thinking it's "blocked". It's not.

在极少数情况下,这可能会导致奇怪的问题.例如,您在绘画时永远不想使用 lock 的原因之一.但这是一个边缘和复杂的案例;但是我已经看到它导致了疯狂的问题.所以为了完整起见,我记下了它.

This can cause bizarre issues in very rare cases. One of the reasons you never want to use a lock when painting, for example. But that's a fringe and complex case; however I've seen it cause crazy issues. So I noted it for completeness sake.

这篇关于Task.Run 带参数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆