如何将其转换为异步任务? [英] How do I convert this to an async task?

查看:32
本文介绍了如何将其转换为异步任务?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

给定以下代码...

static void DoSomething(int id) {
    Thread.Sleep(50);
    Console.WriteLine(@"DidSomething({0})", id);
}

我知道我可以将其转换为异步任务,如下所示...

I know I can convert this to an async task as follows...

static async Task DoSomethingAsync(int id) {
    await Task.Delay(50);
    Console.WriteLine(@"DidSomethingAsync({0})", id);
}

如果我多次调用 (Task.WhenAll),那么一切都会比使用 Parallel.Foreach 甚至从循环内调用更快、更高效.

And that by doing so if I am calling multiple times (Task.WhenAll) everything will be faster and more efficient than perhaps using Parallel.Foreach or even calling from within a loop.

但是,让我们假设 Task.Delay() 不存在,而我实际上必须使用 Thread.Sleep();我知道实际上并非如此,但这是概念代码,延迟/睡眠通常是没有异步选项(例如早期 EF)的 IO 操作.

But for a minute, lets pretend that Task.Delay() does not exist and I actually have to use Thread.Sleep(); I know in reality this is not the case, but this is concept code and where the Delay/Sleep is would normally be an IO operation where there is no async option (such as early EF).

我尝试了以下...

static async Task DoSomethingAsync2(int id) {
    await Task.Run(() => {
        Thread.Sleep(50);
        Console.WriteLine(@"DidSomethingAsync({0})", id);
    });
}

但是,虽然它运行没有错误,根据 Lucien Wischik 这实际上是不好的做法,因为它只是从池中旋转线程来完成每个任务(使用以下控制台应用程序也会变慢 - 如果您在 DoSomethingAsync 和 DoSomethingAsync2 调用之间交换,您可以看到完成所需的时间有显着差异)...

But, though it runs without error, according to Lucien Wischik this is in fact bad practice as it is merely spinning up threads from the pool to complete each task (it is also slower using the following console application - if you swap between DoSomethingAsync and DoSomethingAsync2 call you can see a significant difference in the time that it takes to complete)...

static void Main(string[] args) {
    MainAsync(args).Wait();
}

static async Task MainAsync(String[] args) {

    List<Task> tasks = new List<Task>();
    for (int i = 1; i <= 1000; i++)
        tasks.Add(DoSomethingAsync2(i)); // Can replace with any version
    await Task.WhenAll(tasks);

}

然后我尝试了以下...

I then tried the following...

static async Task DoSomethingAsync3(int id) {
    await new Task(() => {
        Thread.Sleep(50);
        Console.WriteLine(@"DidSomethingAsync({0})", id);
    });
}

用它代替原来的 DoSomethingAsync,测试永远不会完成,屏幕上什么也没有显示!

Transplanting this in place of the original DoSomethingAsync, the test never completes and nothing is shown on screen!

我还尝试了多种其他变体,它们要么无法编译,要么无法完成!

I have also tried multiple other variations that either do not compile or do not complete!

那么,鉴于不能调用任何现有的异步方法并且必须在异步任务中同时完成Thread.Sleep和Console.WriteLine的约束,您如何以与原始代码一样高效的方式进行操作?

So, given the constraint that you cannot call any existing asynchronous methods and must complete both the Thread.Sleep and the Console.WriteLine in an asynchronous task, how do you do it in a manner that is as efficient as the original code?

对于那些有兴趣的人来说,这里的目标是让我更好地了解如何在不调用任何其他人的情况下创建我自己的异步方法.尽管进行了多次搜索,但这似乎是真正缺乏示例的一个领域 - 虽然有成千上万个调用异步方法的示例,这些示例依次调用其他异步方法,但我找不到任何将现有 void 方法转换为异步任务的示例除了使用 Task.Run(() => {} ) 方法的任务之外,没有调用其他异步任务.

The objective here for those of you who are interested is to give me a better understanding of how to create my own async methods where I am not calling anybody elses. Despite many searches, this seems to be the one area where examples are really lacking - whilst there are many thousands of examples of calling async methods that call other async methods in turn I cannot find any that convert an existing void method to an async task where there is no call to a further async task other than those that use the Task.Run(() => {} ) method.

推荐答案

有两种任务:执行代码的任务(例如,Task.Run 和朋友),以及响应一些外部事件(例如,TaskCompletionSource 和朋友).

There are two kinds of tasks: those that execute code (e.g., Task.Run and friends), and those that respond to some external event (e.g., TaskCompletionSource<T> and friends).

您正在寻找的是TaskCompletionSource.常见情况有多种速记"形式,因此您不必总是直接使用 TaskCompletionSource.例如,Task.FromResultTaskFactory.FromAsync.FromAsync 是最常用的,如果你有一个现有的 *Begin/*End 实现你的 I/O;否则,您可以直接使用 TaskCompletionSource.

What you're looking for is TaskCompletionSource<T>. There are various "shorthand" forms for common situations so you don't always have to use TaskCompletionSource<T> directly. For example, Task.FromResult or TaskFactory.FromAsync. FromAsync is most commonly used if you have an existing *Begin/*End implementation of your I/O; otherwise, you can use TaskCompletionSource<T> directly.

有关详细信息,请参阅 实现基于任务的异步模式.

For more information, see the "I/O-bound Tasks" section of Implementing the Task-based Asynchronous Pattern.

Task 构造函数(不幸的是)是基于任务的并行性的保留,不应在异步代码中使用.它只能用于创建基于代码的任务,不能用于创建外部事件任务.

The Task constructor is (unfortunately) a holdover from Task-based parallelism, and should not be used in asynchronous code. It can only be used to create a code-based task, not an external event task.

那么,鉴于不能调用任何现有的异步方法并且必须在异步任务中同时完成Thread.Sleep和Console.WriteLine的约束,您如何以与原始代码一样高效的方式进行操作?

So, given the constraint that you cannot call any existing asynchronous methods and must complete both the Thread.Sleep and the Console.WriteLine in an asynchronous task, how do you do it in a manner that is as efficient as the original code?

我会使用某种计时器并让它在计时器触发时完成一个 TaskCompletionSource.我几乎肯定这就是实际的 Task.Delay 实现所做的.

I would use a timer of some kind and have it complete a TaskCompletionSource<T> when the timer fires. I'm almost positive that's what the actual Task.Delay implementation does anyway.

这篇关于如何将其转换为异步任务?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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