为什么任务取消在调用者线程上发生?

 手机用户2502884625 发布于 2023-01-29 17:17

我在任务取消模式上发现了一个问题,我想了解为什么应该以这种方式工作。

考虑这个小程序,其中辅助线程执行异步“长”任务。同时,主线程通知取消。

该程序是一个较大的程序的非常简化的版本,可以有许多并发线程来执行“长任务”。当用户要求取消时,应取消所有正在运行的任务,因此取消了CancellationTokenSource集合。

class Program
{
    static MyClass c = new MyClass();

    static void Main(string[] args)
    {
        Console.WriteLine("program=" + Thread.CurrentThread.ManagedThreadId);
        var t = new Thread(Worker);
        t.Start();
        Thread.Sleep(500);
        c.Abort();

        Console.WriteLine("Press any key...");
        Console.ReadKey();
    }

    static void Worker()
    {
        Console.WriteLine("begin worker=" + Thread.CurrentThread.ManagedThreadId);

        try
        {
            bool result = c.Invoker().Result;
            Console.WriteLine("end worker=" + result);
        }
        catch (AggregateException)
        {
            Console.WriteLine("canceled=" + Thread.CurrentThread.ManagedThreadId);
        }
    }


    class MyClass
    {
        private List collection = new List();

        public async Task Invoker()
        {
            Console.WriteLine("begin invoker=" + Thread.CurrentThread.ManagedThreadId);

            var cts = new CancellationTokenSource();
            c.collection.Add(cts);

            try
            {
                bool result = await c.MyTask(cts.Token);
                return result;
            }
            finally
            {
                lock (c.collection)
                {
                    Console.WriteLine("removing=" + Thread.CurrentThread.ManagedThreadId);
                    c.collection.RemoveAt(0);
                }
                Console.WriteLine("end invoker");
            }
        }

        private async Task MyTask(CancellationToken token)
        {
            Console.WriteLine("begin task=" + Thread.CurrentThread.ManagedThreadId);
            await Task.Delay(2000, token);
            Console.WriteLine("end task");
            return true;
        }

        public void Abort()
        {
            lock (this.collection)
            {
                Console.WriteLine("canceling=" + Thread.CurrentThread.ManagedThreadId);
                foreach (var cts in collection) //exception here!
                {
                    cts.Cancel();
                }
                //collection[0].Cancel();
            };
        }

    }
}

尽管锁定了收集访问,但是访问它的线程与请求取消的线程相同。也就是说,在迭代过程中修改了集合,并引发了异常。

为了更好的说明,您可以注释掉整个“ foreach”,并取消注释最后一条指令,如下所示:

        public void Abort()
        {
            lock (this.collection)
            {
                Console.WriteLine("canceling=" + Thread.CurrentThread.ManagedThreadId);
                //foreach (var cts in collection) //exception here!
                //{
                //    cts.Cancel();
                //}
                collection[0].Cancel();
            };
        }

这样做也不例外,程序会正常终止。但是,有趣的是所涉及线程的ID:

program=10
begin worker=11
begin invoker=11
begin task=11
canceling=10
removing=10
end invoker
Press any key...
canceled=11

显然,“最终”主体在调用者线程上运行,但是一旦离开“调用者”,该线程将成为辅助线程。

为什么在辅助线程中不执行“ finally”块?

撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有