【问题标题】:Is it ok to await the same task from multiple threads - is await thread safe?可以从多个线程等待相同的任务 - 等待线程安全吗?
【发布时间】:2013-05-29 17:32:02
【问题描述】:

等待线程安全吗?似乎 Task 类是线程安全的,所以我想等待它也是线程安全的,但我没有在任何地方找到确认。线程安全也是自定义等待器的要求 - 我的意思是 IsCompleted、GetAwaiter 等方法? IE。如果这些方法不是线程安全的,那么等待是线程安全的吗?但是,我预计不会很快需要自定义等待器。

用户场景示例:假设我有一个异步返回结果的后台任务,然后从多个线程中使用:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace scratch1
{
    class Foo
    {
        Task<int> _task;

        public Foo()
        {
            _task = Task.Run(async () => { await Task.Delay(5000); return 5; });
        }

        // Called from multiple threads
        public async Task<int> Bar(int i)
        {
            return (await _task) + i;
        }
    }

    class Program
    {
        public static void Main(string[] args)
        {
            Foo foo = new Foo();

            List<Task> tasks = new List<Task>();
            foreach (var i in Enumerable.Range(0, 100))
            {
                tasks.Add(Task.Run(async () => { Console.WriteLine(await foo.Bar(i)); })); 
            }

            Task.WaitAll(tasks.ToArray());
        }
    }
}

【问题讨论】:

    标签: c# asynchronous thread-safety async-await


    【解决方案1】:

    正如你所提到的,await 足够薄,一开始就看不到线。

    await 相关的代码(状态机中编译器生成的代码,以及BCL 中的支持类)一次只能在一个线程上运行。 (从等待返回时可以切换到不同的线程,但之前的运行已经完成)

    实际的线程安全取决于您等待的对象。
    它必须有一种线程安全的方式来添加回调(或者await一次两次可能会中断)。

    此外,它必须只调用一次回调,否则可能会破坏状态机。 (特别是,如果它同时在两个线程上运行它的回调,事情会变得非常糟糕)

    Task 是线程安全的。

    【讨论】:

    • 您的第一个陈述可能会误导人们认为await 从根本上来说是不安全的......而我认为您的意思是它既不是本质上的安全也不是不安全的——它只是代表正在等待的任何东西,如果那是@ 987654326@,你没事。
    • 当然,谢谢。虽然“将只在一个线程上运行”可能应该是“每次只在一个线程上运行,每次方法调用(对应于状态机的单个实例)”。它当然可以在await 点的线程之间跳来跳去,但它不会在多个线程上同时运行。 (至少不会,除非你严重滥用它......这总是很有趣。)
    猜你喜欢
    • 1970-01-01
    • 2015-08-23
    • 1970-01-01
    • 1970-01-01
    • 2023-03-11
    • 1970-01-01
    • 1970-01-01
    • 2015-07-13
    相关资源
    最近更新 更多