Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/278.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何从工作线程轮询取消_C#_Multithreading_Cancellation - Fatal编程技术网

C# 如何从工作线程轮询取消

C# 如何从工作线程轮询取消,c#,multithreading,cancellation,C#,Multithreading,Cancellation,我有一个UI,它由一个后台工作线程生成,该线程执行一个复杂的任务树和子任务,大约需要一分钟才能完成 一个要求是后台工作任务必须能够在其开始后被取消 目前,我的解决方案还很幼稚,把代码弄得一团糟。在UI中按下取消按钮时,将设置取消令牌。工作线程定期(在任务之间)轮询此令牌,如果已设置,则它将退出: void ThreadWorkerHandler(CancelToken cancelToken) { DoTask1(cancelToken); if (cancelToken.IsS

我有一个UI,它由一个后台工作线程生成,该线程执行一个复杂的任务树和子任务,大约需要一分钟才能完成

一个要求是后台工作任务必须能够在其开始后被取消

目前,我的解决方案还很幼稚,把代码弄得一团糟。在UI中按下取消按钮时,将设置取消令牌。工作线程定期(在任务之间)轮询此令牌,如果已设置,则它将退出:

void ThreadWorkerHandler(CancelToken cancelToken)
{
    DoTask1(cancelToken);
    if (cancelToken.IsSet)
        return;
    DoTask2(cancelToken);
    if (cancelToken.IsSet)
        return;
    DoTask3(cancelToken);
    if (cancelToken.IsSet)
        return;
    DoTask4(cancelToken);
}

void DoTask2(CancelToken cancelToken)
{
    DoSubTask2a();
    if (cancelToken.IsSet)
        return;
    DoSubTask2b();
    if (cancelToken.IsSet)
        return;
    DoSubTask2c();
    if (cancelToken.IsSet)
        return;
}
有更好的解决办法吗?我在想一个类似SoLongAs语句的东西,它会自动插入检查,并在满足条件时自动引发内部异常,该异常会在循环结束时被内部捕获,例如:

void ThreadWorkerHandler(CancelToken cancelToken)
{
    SoLongAs (canelToken.IsSet == false)
    {
        DoTask1(cancelToken);
        DoTask2(cancelToken);
        DoTask3(cancelToken);
        DoTask4(cancelToken);
    }
}

但我想,出于某种原因,这是行不通的,更重要的是,我怀疑这样的事情是否真的存在。如果没有,有没有比我目前使用的更好的方法来处理这个场景?谢谢。

如果您有一组代表您的工作的委托,那么您可以得到一些与您的代码片段非常接近的内容。它的开销比您预期的语法要多一些,但关键是它是一个恒定的开销,而不是每行的开销

List<Action> actions = new List<Action>()
{
    ()=> DoTask1(cancelToken),
    ()=> DoTask2(cancelToken),
    ()=> DoTask3(cancelToken),
    ()=> DoTask4(cancelToken),
};

foreach(var action in actions)
{
    if (!cancelToken.IsSet)
        action();
}
List actions=new List()
{
()=>DoTask1(取消令牌),
()=>DoTask2(取消令牌),
()=>DoTask3(取消令牌),
()=>DoTask4(取消令牌),
};
foreach(行动中的var行动)
{
如果(!cancelToken.IsSet)
动作();
}

如果您有一组代表您的工作的委托,那么您可以得到与您的代码片段非常接近的内容。它的开销比您预期的语法要多一些,但关键是它是一个恒定的开销,而不是每行的开销

List<Action> actions = new List<Action>()
{
    ()=> DoTask1(cancelToken),
    ()=> DoTask2(cancelToken),
    ()=> DoTask3(cancelToken),
    ()=> DoTask4(cancelToken),
};

foreach(var action in actions)
{
    if (!cancelToken.IsSet)
        action();
}
List actions=new List()
{
()=>DoTask1(取消令牌),
()=>DoTask2(取消令牌),
()=>DoTask3(取消令牌),
()=>DoTask4(取消令牌),
};
foreach(行动中的var行动)
{
如果(!cancelToken.IsSet)
动作();
}

您可以使用
CancellationToken.throwifccancellationRequested()
。如果设置了令牌,这将引发异常

也可以考虑使用<代码> TPL <代码>任务< /代码>。所有子任务都可以使用相同的

CancellationToken
一个接一个地链接,这将简化代码,因为
TPL
框架在调用continuation之前会注意检查
Token
状态

您的代码如下所示:

Task.Factory.StartNew(DoTask1,cancelationToken)
.ContinueWith(t=>DoTask2(),cancelationToken)
.ContinueWith(t=>DoTask3(),cancelationToken)
.ContinueWith(t=>DoTask4(),cancelationToken)
注意此解决方案假定
DoTask
不会引发除
操作取消异常
之外的其他异常


Note2您不必在任务/子任务正文内调用
ThrowIfCancellationRequested()
<代码>TPL将在调用任何continuations之前自动检查令牌状态。但是您可以使用此方法中断任务/子任务的执行。

您可以使用
取消令牌。ThrowIfCancellationRequested()
。如果设置了令牌,这将引发异常

也可以考虑使用<代码> TPL <代码>任务< /代码>。所有子任务都可以使用相同的

CancellationToken
一个接一个地链接,这将简化代码,因为
TPL
框架在调用continuation之前会注意检查
Token
状态

您的代码如下所示:

Task.Factory.StartNew(DoTask1,cancelationToken)
.ContinueWith(t=>DoTask2(),cancelationToken)
.ContinueWith(t=>DoTask3(),cancelationToken)
.ContinueWith(t=>DoTask4(),cancelationToken)
注意此解决方案假定
DoTask
不会引发除
操作取消异常
之外的其他异常


Note2您不必在任务/子任务正文内调用
ThrowIfCancellationRequested()
<代码>TPL将在调用任何continuations之前自动检查令牌状态。但是您可以使用此方法中断任务/子任务的执行。

Servy的想法非常好。我只是偷了它(要归功于他!),并演示了如何将它与
列表的扩展方法一起使用。我完全理解任何认为这“太可爱”的人,但我认为它有一定的优雅

这里有一个练习,演示如何使用扩展方法。根据Servy的想法,扩展获取一个动作委托列表,并依次运行每个动作委托,直到完成或取消

private static bool test(CancellationToken cancelToken)
{
    return new List<Action> 
    { 
        doTask1,
        doTask2,
        doTask3,
        doTask4,
        () => Console.WriteLine("Press a key to exit.")
    }
    .Run(cancelToken);
}
专用静态布尔测试(CancellationToken cancelToken)
{
返回新列表
{ 
doTask1,
doTask2,
doTask3,
doTask4,
()=>Console.WriteLine(“按一个键退出”)
}
.运行(取消令牌);
}
下面是整个样本:

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

    namespace ConsoleApplication2
    {
        internal class Program
        {
            private static void Main(string[] args)
            {
                CancellationTokenSource cancelSource = new CancellationTokenSource();

                Console.WriteLine("Press any key to interrupt the work.");
                var work = Task<bool>.Factory.StartNew(() => test(cancelSource.Token));
                Console.ReadKey();
                cancelSource.Cancel();
                Console.WriteLine(work.Result ? "Completed." : "Interrupted.");
            }

            private static bool test(CancellationToken cancelToken)
            {
                return new List<Action> 
                { 
                    doTask1,
                    doTask2,
                    doTask3,
                    doTask4,
                    () => Console.WriteLine("Press a key to exit.")
                }
                .Run(cancelToken);
            }

            private static void doTask1()
            {
                Console.WriteLine("Task 1 Working...");
                Thread.Sleep(1000);
                Console.WriteLine("...did some work.");
            }

            private static void doTask2()
            {
                Console.WriteLine("Task 2 Working...");
                Thread.Sleep(1000);
                Console.WriteLine("...did some work.");
            }

            private static void doTask3()
            {
                Console.WriteLine("Task 3 Working...");
                Thread.Sleep(1000);
                Console.WriteLine("...did some work.");
            }

            private static void doTask4()
            {
                Console.WriteLine("Task 4 Working...");
                Thread.Sleep(1000);
                Console.WriteLine("...did some work.");
            }
        }

        public static class EnumerableActionExt
        {
            public static bool Run(this IEnumerable<Action> actions, CancellationToken cancelToken)
            {
                foreach (var action in actions)
                {
                    if (!cancelToken.IsCancellationRequested)
                    {
                        action();
                    }
                    else
                    {
                        return false;
                    }
                }

                return true;
            }
        }
    }
使用系统;
使用System.Collections.Generic;
使用系统线程;
使用System.Threading.Tasks;
命名空间控制台应用程序2
{
内部课程计划
{
私有静态void Main(字符串[]args)
{
CancellationTokenSource cancelSource=新的CancellationTokenSource();
控制台。WriteLine(“按任意键中断工作”);
var work=Task.Factory.StartNew(()=>test(cancelSource.Token));
Console.ReadKey();
cancelSource.Cancel();
Console.WriteLine(work.Result?“已完成”。:“中断”);
}
专用静态布尔测试(CancellationToken cancelToken)
{
返回新列表
{ 
doTask1,