【问题标题】:Can I wrap Task.Run under another Task.Run()?我可以将 Task.Run 包装在另一个 Task.Run() 下吗?
【发布时间】:2014-03-07 17:02:38
【问题描述】:

我有一个位于 Task.Run() 下的方法 HandleAcceptedConnection,我想异步运行(在另一个单独的线程中)。我尝试将 HandleAcceptedConnection 声明为异步方法并且不调用 await 但它似乎没有异步运行。我可以确认我可以在另一个 Task.Run() 下拥有 Task.Run()(通过查看线程 id),但这是推荐的吗?

private async void Start_Click(object sender, RoutedEventArgs e)
{
        var task = Task.Run(() =>
        {
            while (isContinue)
            {
                var handler = listener.Accept();
                // handle connection

                Log("Before");
                Log("ThreadId Accept " + Thread.CurrentThread.ManagedThreadId);

                // i want to run method below asynchronously. i want to 
                // wrap it under Task.Run() but i am already under 
                // Task.Run(). i set HandleAcceptedConnection as async. i thought by not 
                // calling await on HandleAcceptedConnection, HandleAcceptedConnection 
                // is asynchronous
                HandleAcceptedConnection(handler); 

                Log("After");

                isContinue = true;
            }
        });
        await task;
}

private async Task HandleAcceptedConnection(Socket handler)
{
    Log("ThreadId HandleAcceptedConnection " + Thread.CurrentThread.ManagedThreadId);
    Log("Under HandleAcceptedConnection");
    Thread.Sleep(10000);   
}

当我运行它时,日志说

Before
Under HandleAcceptedConnection
After

我想要

Before
After
Under HandleAcceptedConnection

我希望 HandleAcceptedConnection 异步运行。我应该将它包装在另一个 Task.Run 下还是它已经是异步的?

【问题讨论】:

  • 您的方法将同步运行,因为它不包含await 语句。但即使它确实异步运行,这也意味着您根本无法保证“After”和“Under”会出现在哪个顺序。如果您需要 “After”出现在“Under”之前,只需将方法调用移动到末尾即可。您想在这里实现什么目标?
  • 我认为您需要更深入地阅读 async/await/Tasks。主要要理解:async/await/Task 不一定等于多线程。
  • @Jon "Before" "after" "Under" 只是指定 HandleAcceptedConnection 是同步运行还是异步运行。了解 HandleAcceptedConnection 是否异步运行的更好方法是监视线程(我刚刚添加)。我想要的只是在“上面的调用者”的另一个线程/异步中运行 HandleAcceptedConnection。
  • @publicENEMY 现在将 Thread.Sleep(10000); 替换为 await Task.Delay(10000); 并重试。顺便说一句:在 Logging 之前使用它可以看到它会在 after Log("after") 运行
  • @L.B 做到了。我还在 Task.Delay() 之前和之后放置了 Thread.CurrentThread.ManagedThreadId ,并且似乎在 Task.Delay() 之前和之后使用了不同的线程。似乎 Task.Delay() 导致该方法使用不同的线程。

标签: c# multithreading asynchronous task-parallel-library


【解决方案1】:

你试过了吗

private async Task HandleAcceptedConnection(Socket handler)
{
    Thread.Sleep(1000);
    Log("Under HandleAcceptedConnection");
}

因为在另一个线程上做某事并不意味着它会被延迟。

【讨论】:

  • 更好地使用Task.Delay() - 它不会因为阻塞而浪费线程。然后你将不得不等待它 - 否则首先创建方法 async 没有多大意义。
  • @Krumelur 不仅更好。他/她必须使用await Task.Delay(1000); 才能获得所期望的效果。
  • @L.B 我不明白他要解决什么问题,所以我不确定想要的效果是什么:-)
  • @Krumelur OP 不想等待HandleAcceptedConnection
  • 没错。如果他不想等待,为什么要让它异步开始呢?我想说的是:提问者试图解决一些听起来不像是经过适当考虑的问题,并且是对多线程、ATPL、同步和异步代码的误解和混合。
【解决方案2】:

你应该使用AcceptTcpClientAsync,那么你就不需要额外的线程了。以this answer 为例。如果有可用的自然异步版本,请不要使用同步 API。

已更新以解决评论问题。没有什么能阻止您从 Task.Run 内部使用 Task.Run,您的代码可能如下所示(未经测试):

private async void Start_Click(object sender, RoutedEventArgs e)
{
    var connectionTasks = new List<Task>();

    Func<Task> handleConnection = async () =>
    {
        var connectionTask = Task.Run(() => HandleAcceptedConnection(handler));
        connectionTasks.Add(connectionTask);
        await connectionTask;
        connectionTasks.Remove(connectionTask);
    };

    var task = Task.Run(() =>
    {
        while (isContinue)
        {
            var handler = listener.Accept();
            // handle connection

            Log("Before");
            Log("ThreadId Accept " + Thread.CurrentThread.ManagedThreadId);

            var connectionTask = handleConnection();

            Log("After");

            isContinue = true;
        }
    });
    await task;
}

【讨论】:

  • 我使用的网络库中没有AcceptTcpClientAsync。事实上,所有暴露的方法在我使用的网络库中都是同步的。
  • @publicENEMY,我明白了。你应该在你的问题中澄清这一点。我现在回想起另一个问题,您提到了一个支持异步的 3rd 方套接字类库,但明天我不会记得了 :)
  • 哈哈。对不起。我不想提及任何关于套接字或网络的事情,因为问题实际上是关于异步的。
  • @publicENEMY,没问题。请注意,只有在其中使用 await 时,您才需要在 lambda 之前使用 async - 像往常一样。
  • @publicENEMY,确实,在你的情况下,每个新连接都意味着一个新的池线程,所以你不应该awaitTask.Run 的结果。不过,您仍然需要跟踪连接。我已经更新了答案以说明如何完成。
猜你喜欢
  • 2014-08-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-22
  • 2012-10-15
  • 1970-01-01
  • 2015-05-09
  • 1970-01-01
相关资源
最近更新 更多