C# 停止dll循环

C# 停止dll循环,c#,multithreading,dll,loops,C#,Multithreading,Dll,Loops,我有一个多线程C#应用程序,它在dll中使用一些递归函数。我的问题是如何干净地停止递归函数 递归函数用于遍历SCADA系统的分层“SCADA对象”数据。遍历数据需要很长的时间(10分钟),这取决于系统的大小以及我们需要对数据做什么 当我开始工作时,我会创建一个后台线程,以便GUI保持响应。然后后台工作程序处理dll中递归函数的调用 我可以使用CancelAsync向后台工作程序发送取消请求,但后台工作程序无法检查CancellationPending标志,因为它在等待dll的递归函数完成时被阻止

我有一个多线程C#应用程序,它在dll中使用一些递归函数。我的问题是如何干净地停止递归函数

递归函数用于遍历SCADA系统的分层“SCADA对象”数据。遍历数据需要很长的时间(10分钟),这取决于系统的大小以及我们需要对数据做什么

当我开始工作时,我会创建一个后台线程,以便GUI保持响应。然后后台工作程序处理dll中递归函数的调用

我可以使用CancelAsync向后台工作程序发送取消请求,但后台工作程序无法检查CancellationPending标志,因为它在等待dll的递归函数完成时被阻止

通常一次只有一个递归函数处于活动状态,但是有几十个递归函数在不同的时间被不同的后台工作人员使用

作为一个快速(而且非常可耻)的黑客,我在dll中添加了一个全局“CodeEnabled”标志。因此,当GUI执行CancelAsync时,它还将“CodeEnabled”标志设置为false。(我知道我需要一些糟糕的代码偏移量)。然后dll的递归循环检查“CodeEnabled”标志并返回到后台工作程序,该后台工作程序最终能够停止

我不想将递归逻辑移动到后台工作线程,因为我在其他地方需要它(例如,其他后台工作线程)


对于这种类型的问题,应该使用什么其他方法?

这实际上取决于设计。许多递归可以用(例如)本地堆栈(
stack
)或队列(
queue
)来代替,在这种情况下,可以在本地保留取消标志,而不会带来太多麻烦。另一个选项是使用某种进度事件,允许订阅者设置取消标志。第三个选项是将某种上下文类传递到函数中,并设置(volatile或synchronized)标志

在这些情况下,您应该可以相对轻松地访问cancel标志以退出递归

FooContext ctx = new FooFontext();
BeginSomeRecursiveFunction(ctx);
...
ctx.Cancel = true; // or ctx.Cancel(), whatever
使用(在接受上下文的函数中):


另一个有趣的选择是使用而不是常规代码;然后,调用代码可以在已经足够的时候停止迭代。

这实际上取决于设计。许多递归可以用(例如)本地堆栈(
stack
)或队列(
queue
)来代替,在这种情况下,可以在本地保留取消标志,而不会带来太多麻烦。另一个选项是使用某种进度事件,允许订阅者设置取消标志。第三个选项是将某种上下文类传递到函数中,并设置(volatile或synchronized)标志

在这些情况下,您应该可以相对轻松地访问cancel标志以退出递归

FooContext ctx = new FooFontext();
BeginSomeRecursiveFunction(ctx);
...
ctx.Cancel = true; // or ctx.Cancel(), whatever
使用(在接受上下文的函数中):


另一个有趣的选择是使用而不是常规代码;然后,您的调用代码可以在已经足够的时候停止迭代。

嗯,在我看来,您需要将“立即停止”状态传播到递归调用中。您可以拥有某种取消令牌,将其传递给递归调用,并在UI线程中保留。像这样简单的事情:

public class CancellationToken
{
    private volatile bool cancelled;

    public bool IsCancelled { get { return cancelled; } }
    public void Cancel() { cancelled = true; }
}
(我越来越担心易变性和无锁编码;我很想在这里使用锁而不是易变性变量,但为了简单起见,我把它保留在这里。)

因此,您需要创建取消令牌,将其传入,然后在每个递归方法调用的开始处,您将有:

if (token.IsCancelled)
{
    return null; // Or some other dummy value, or throw an exception
}
然后在UI线程中调用
Cancel()
。基本上,这只是一种分享“这项任务是否应该继续”状态的方式


选择是传播一个伪返回值还是抛出一个异常是一个有趣的选择。在某些方面,这并不例外-您必须部分地期待它,否则您不会首先传递取消令牌-但同时,异常具有您想要的行为,即将堆栈展开到可以轻松识别取消的地方。

在我看来,您需要传播“立即停止”状态下的递归调用。您可以拥有某种取消令牌,将其传递给递归调用,并在UI线程中保留。简单如下:

public class CancellationToken
{
    private volatile bool cancelled;

    public bool IsCancelled { get { return cancelled; } }
    public void Cancel() { cancelled = true; }
}
(我越来越担心易变性和无锁编码;我很想在这里使用锁而不是易变性变量,但为了简单起见,我把它保留在这里。)

因此,您需要创建取消令牌,将其传入,然后在每个递归方法调用的开始处,您将有:

if (token.IsCancelled)
{
    return null; // Or some other dummy value, or throw an exception
}
然后您只需在UI线程中调用
Cancel()
,基本上这只是一种共享“此任务是否继续”状态的方式


选择是传播一个伪返回值还是抛出一个异常是一个有趣的选择。在某些方面,这并不是异常-您必须部分地期待它,否则您不会首先传递取消标记-但同时异常具有您想要的行为,即将堆栈解绑到某个位置我喜欢前面的答案,但这里有另一个答案

我想你是在问如何为不同的线程设置不同的取消标志

假设您可能要取消的每个线程都有某种类型的ThreadId,那么您可以拥有一个全局线程安全的标志字典,其中TheadId值用作字典的键,而不是一个全局的“CodeEnabled”标志


然后,一个线程将查询字典,查看它的标志是否已设置。

我喜欢前面的答案,但这里有另一个答案

我想你是在问如何为不同的线程设置不同的取消标志

假设