在 C# 中访问多线程列表时的竞争条件

Race condition in accessing a list of mutliple threads in C#

private FruitResult GetFruitResult(Fruits fruits)
{
    List<Thread> threads = new List<Thread>();
    foreach (string fruit in fruits)
    {
        Thread t = new Thread(() => CheckFruitQuality(fruit, someOtherThings, ref fruitResult));
        threads.Add(t);
    }

    threads.ForEach(x => x.Start());
    threads.ForEach(x => x.Join());

    return fruitResult;
}

private void CheckFruitQuality(string fruit, SomeOtherThings someOtherThings, ref FruitResult fruitResult)
{
    ......
    get size and color
    ......

    Result r = new Result();
    r.Size = size;
    r.Color = color;

    fruitResult.FruitReports.Add(r);
}

由于 Thread 构造函数只接受委托,因此我不得不 return fruitResult 作为参考。现在,如何锁定 fruitResult,这样就不会出现竞争条件?

您应该尽可能避免在线程之间共享状态。锁定会降低性能和可伸缩性。手动创建线程也不是最佳实践。创建线程的成本非常高,您的 CPU 只能同时处理有限数量的线程。最好让线程池为你管理线程。使用 ref 参数使代码的可读性低于简单地从方法返回值。

所有这 3 个问题都可以通过使用 tasks 而不是线程来解决。然后你可以很容易地在不使用 ref 参数的情况下取回结果。如果 CheckFruitQuality 是计算密集型操作,您可以尝试这样的操作:

private async Task<FruitResult> GetFruitResult(IEnumerable<string> fruits)
{
    var fruitResult = new FruitResult();
    // start the tasks
    var tasks = fruits.Select(fruit => Task.Run(() => CheckFruitQuality(fruit, someOtherThings)));

    // wait for them to complete
    var results = await Task.WhenAll(tasks);

    //process results
    foreach (var result in results)
    {
        fruitResult.FruitReports.Add(result);
    }

    return fruitResult;
}

private Result CheckFruitQuality(string fruit, SomeOtherThings someOtherThings)
{
    // get size and color

    return new Result()
    {
        Size = size,
        Color = color
    };
}

如果 CheckFruitQuality 是一个 I/O 操作,那么您可能甚至不需要并行性,只需使用异步 I/O。

任务在线程池线程上执行,因此比手动创建新线程效率更高。