簡體   English   中英

為什么使用 CancellationTokenSource.Cancel 的多線程代碼需要較少的反重排序措施

[英]Why does multithreaded code using CancellationTokenSource.Cancel require less anti-reordering measures

我試圖理解為什么這里的共享CancellationTokenSource變量不受鎖或內存屏障的保護。

我知道有一個經驗法則,如果允許編譯器優化,共享(狀態)變量的讀取或寫入可以與局部變量讀取和寫入重新排序。

這是CancellationTokenSource 文檔中的一個示例。

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

public class Example
{
    public static void Main()
    {
        // Define the cancellation token.
        CancellationTokenSource source = new CancellationTokenSource();
        CancellationToken token = source.Token;

        Random rnd = new Random();
        Object lockObj = new Object();

        List<Task<int[]>> tasks = new List<Task<int[]>>();
        TaskFactory factory = new TaskFactory(token);
        for (int taskCtr = 0; taskCtr <= 10; taskCtr++)
        {
            int iteration = taskCtr + 1;
            tasks.Add(factory.StartNew(() =>
            {
                int value;
                int[] values = new int[10];
                for (int ctr = 1; ctr <= 10; ctr++)
                {
                    lock (lockObj)
                    {
                        value = rnd.Next(0, 101);
                    }
                    if (value == 0)
                    {
                        source.Cancel();
                        Console.WriteLine("Cancelling at task {0}", iteration);
                        break;
                    }
                    values[ctr - 1] = value;
                }
                return values;
            }, token));
        }
        try
        {
            Task<double> fTask = factory.ContinueWhenAll(tasks.ToArray(), (results) =>
            {
                Console.WriteLine("Calculating overall mean...");
                long sum = 0;
                int n = 0;
                foreach (var t in results)
                {
                    foreach (var r in t.Result)
                    {
                        sum += r;
                        n++;
                    }
                }
                return sum / (double)n;
            }, token);
            Console.WriteLine("The mean is {0}.", fTask.Result);
        }
        catch (AggregateException ae)
        {
            foreach (Exception e in ae.InnerExceptions)
            {
                if (e is TaskCanceledException)
                    Console.WriteLine("Unable to compute mean: {0}",
                                      ((TaskCanceledException)e).Message);
                else
                    Console.WriteLine("Exception: " + e.GetType().Name);
            }
        }
        finally
        {
            source.Dispose();
        }
    }
}

這樣做的確切原因是什么? Microsoft 是否暗示這種重新排序是安全的,因此不需要保護措施,或者根本不可能重新排序?

正如The Old New Thing的作者在他的評論中所指出的, source.Cancel(); 放置在多線程代碼中的指令通過其內部實現來防止重新排序。

https://referencesource.microsoft.com/#mscorlib/system/threading/CancellationTokenSource.cs,723指出 CancellationTokenSource 依賴於 Interlocked 類方法。

根據 Joe Albahari 的說法,C# 中 Interlocked 類上的所有方法都會隱式生成完整的圍欄: http : //www.albahari.com/threading/part4.aspx#_Memory_Barriers_and_Volatility

因此,如果在多個任務訪問時需要保護它,則可以在委托主體內自由調用 CancellationTokenSource.Cancel 方法,而無需額外的鎖或內存屏障。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM