137

我一直在尝试获得一些我认为使用 .NET 4.5 会很简单的东西

我想同时启动两个长时间运行的任务并
以最佳 C# 4.5 (RTM) 方式收集结果

以下工作,但我不喜欢它,因为:

  • 我想Sleep成为一个异步方法,所以它可以await使用其他方法
  • 它看起来很笨拙Task.Run()
  • 我认为这甚至根本没有使用任何新的语言功能!

工作代码:

public static void Go()
{
    Console.WriteLine("Starting");

    var task1 = Task.Run(() => Sleep(5000));    
    var task2 = Task.Run(() => Sleep(3000));

    int totalSlept = task1.Result + task2.Result;

    Console.WriteLine("Slept for a total of " + totalSlept + " ms");
}

private static int Sleep(int ms)
{
    Console.WriteLine("Sleeping for " + ms);
    Thread.Sleep(ms);
    Console.WriteLine("Sleeping for " + ms + " FINISHED");
    return ms;
}

非工作代码:

更新:这实际上有效并且是正确的方法,唯一的问题是Thread.Sleep

此代码不起作用,因为调用Sleep(5000)立即启动任务运行,因此Sleep(1000)在完成之前不会运行。这是真的,即使Sleepasync而且我没有使用await或调用.Result太快。

Task<T>我想也许有一种方法可以通过调用一个方法来获得非运行,async这样我就可以调用Start()这两个任务,但我不知道如何Task<T>从调用异步方法中获得一个。

public static void Go()
{
    Console.WriteLine("Starting");

    var task1 = Sleep(5000);    // blocks
    var task2 = Sleep(1000);

    int totalSlept = task1.Result + task2.Result;

    Console.WriteLine("Slept for " + totalSlept + " ms");
}

private static async Task<int> Sleep(int ms)
{
    Console.WriteLine("Sleeping for " + ms);
    Thread.Sleep(ms);
    return ms;
}
4

6 回答 6

136
async Task<int> LongTask1() { 
  ...
  return 0; 
}

async Task<int> LongTask2() { 
  ...
  return 1; 
}

...
{
   Task<int> t1 = LongTask1();
   Task<int> t2 = LongTask2();
   await Task.WhenAll(t1,t2);
   //now we have t1.Result and t2.Result
}
于 2012-11-24T14:20:18.630 回答
100

您应该使用 Task.Delay 而不是 Sleep 进行异步编程,然后使用 Task.WhenAll 来组合任务结果。这些任务将并行运行。

public class Program
    {
        static void Main(string[] args)
        {
            Go();
        }
        public static void Go()
        {
            GoAsync();
            Console.ReadLine();
        }
        public static async void GoAsync()
        {

            Console.WriteLine("Starting");

            var task1 = Sleep(5000);
            var task2 = Sleep(3000);

            int[] result = await Task.WhenAll(task1, task2);

            Console.WriteLine("Slept for a total of " + result.Sum() + " ms");

        }

        private async static Task<int> Sleep(int ms)
        {
            Console.WriteLine("Sleeping for {0} at {1}", ms, Environment.TickCount);
            await Task.Delay(ms);
            Console.WriteLine("Sleeping for {0} finished at {1}", ms, Environment.TickCount);
            return ms;
        }
    }
于 2012-09-09T23:43:53.753 回答
3

虽然您的Sleep方法是异步的,Thread.Sleep但不是。异步的整个想法是重用单个线程,而不是启动多个线程。因为您已阻止使用对 Thread.Sleep 的同步调用,所以它不会工作。

我假设这Thread.Sleep是对您实际想要做的事情的简化。您的实际实现可以编码为异步方法吗?

如果您确实需要运行多个同步阻塞调用,我想看看别处!

于 2012-09-09T21:25:19.267 回答
2

要回答这个问题:

我希望 Sleep 是一个异步方法,因此它可以等待其他方法

你也许可以Sleep像这样重写函数:

private static async Task<int> Sleep(int ms)
{
    Console.WriteLine("Sleeping for " + ms);
    var task = Task.Run(() => Thread.Sleep(ms));
    await task;
    Console.WriteLine("Sleeping for " + ms + "END");
    return ms;
}

static void Main(string[] args)
{
    Console.WriteLine("Starting");

    var task1 = Sleep(2000);
    var task2 = Sleep(1000);

    int totalSlept = task1.Result +task2.Result;

    Console.WriteLine("Slept for " + totalSlept + " ms");
    Console.ReadKey();
}

运行此代码将输出:

Starting
Sleeping for 2000
Sleeping for 1000
*(one second later)*
Sleeping for 1000END
*(one second later)*
Sleeping for 2000END
Slept for 3000 ms
于 2013-06-12T22:38:31.270 回答
2

现在是周末

    public async void Go()
    {
        Console.WriteLine("Start fosterage...");

        var t1 = Sleep(5000, "Kevin");
        var t2 = Sleep(3000, "Jerry");
        var result = await Task.WhenAll(t1, t2);

        Console.WriteLine($"My precious spare time last for only {result.Max()}ms");
        Console.WriteLine("Press any key and take same beer...");
        Console.ReadKey();
    }

    private static async Task<int> Sleep(int ms, string name)
    {
            Console.WriteLine($"{name} going to sleep for {ms}ms :)");
            await Task.Delay(ms);
            Console.WriteLine("${name} waked up after {ms}ms :(";
            return ms;
    }
于 2013-12-14T15:47:15.927 回答
0

这篇文章帮助解释了很多事情。它采用常见问题解答样式。

异步/等待常见问题解答

这部分解释了为什么Thread.Sleep在同一个原始线程上运行 - 导致我最初的困惑。

“async”关键字是否会导致方法调用排队到 ThreadPool?创建一个新线程?向火星发射火箭飞船?

不,不,不。请参阅前面的问题。“async”关键字向编译器指示可以在方法内部使用“await”,以便该方法可以在等待点挂起,并在等待的实例完成时异步恢复其执行。这就是为什么如果在标记为“异步”的方法中没有“等待”,编译器会发出警告。

于 2012-09-10T01:00:43.783 回答