首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么使用CancellationTokenSource.Cancel的多线程代码需要更少的反重新排序措施?

为什么使用CancellationTokenSource.Cancel的多线程代码需要更少的反重新排序措施?
EN

Stack Overflow用户
提问于 2021-10-12 16:05:23
回答 1查看 121关注 0票数 0

我试图理解为什么共享的CancellationTokenSource变量在这里不受锁或内存屏障的保护。

我知道,如果允许编译器优化,共享(状态)变量的读或写可以与本地变量重新排序。

下面是CancellationTokenSource 文档的一个示例。

代码语言:javascript
复制
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();
        }
    }
}

具体原因是什么?微软是否暗示这样的重新排序是安全的,因此不需要任何保护措施,或者根本不可能重新排序?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-10-13 21:49:16

正如The Old New Thing的作者在他的评论中所指出的,放置在多线程代码中的source.Cancel();指令通过其内部实现不被重新排序。

https://referencesource.microsoft.com/#mscorlib/system/threading/CancellationTokenSource.cs,723声明CancellationTokenSource依赖于互锁类方法。

根据Joe的说法,C#中的互锁类上的所有方法都隐式地生成完整的栅栏:波动率

因此,如果在被多个任务访问时需要保护CancellationTokenSource.Cancel方法,则可以在委托主体中自由地放置对它的调用,而无需额外的锁或内存屏障。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69543717

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档