检测线程内的布尔值变化

Posted

技术标签:

【中文标题】检测线程内的布尔值变化【英文标题】:Detect Boolean value changes inside Thread 【发布时间】:2014-06-27 16:40:54 【问题描述】:

我有一个要在 C# 线程中运行的 c++ dll 函数。

有时我需要取消该线程,这是问题所在:

Thread.Abort() 在我读过的大量文章中是邪恶的 主题

这样做的唯一方法是使用布尔值并定期检查它的值。

我的问题是,即使我将这个值设置为 true,它也没有改变,在 c++ 代码中仍然等于 false。但是,当我显示 MessageBox 时,该值发生了变化并且工作正常。 任何想法为什么该值仅在 MessageBox 显示时才发生变化,请告诉我如何解决该问题。 C#

 public void AbortMesh()
         
            if (currMeshStruct.Value.MeshThread != null && currMeshStruct.Value.MeshThread.IsAlive)
            
             //here is my c++ Object and cancel mesh used to set bool to true;                   
             MeshCreator.CancelMesh();
            
        

C++

STDMETHODIMP MeshCreator::CancelMesh(void)

    this->m_StopMesh = TRUE;
    return S_OK;

当我测试布尔值时

if (m_StopMesh)
    return S_FALSE;

即使我调用 AbortMesh(),此处的值也始终为 false

if (m_StopMesh)
        return S_FALSE;

 MessageBox(NULL,aMessage,L"Test",NULL);

 if (m_StopMesh) // here the value is changed to true 
        return S_FALSE;

【问题讨论】:

" 我使用线程启动了 c++ 进程,它运行良好。"并且“在 c++ 代码中它仍然等于 false”在一个语句中看起来很奇怪。如果确实是进程,您如何期望局部变量神奇地跨越进程边界?无论如何,您需要添加显示问题的代码 - 没有它就无法猜测。 另外,Thread.Abort() 将中止线程,而不是进程。听起来您的 C++ 代码在单独的进程中运行。 Thread.Abort() 不会终止该进程。 它是您启动的单独的 c++ 应用程序吗?还是您在 C# 线程中运行的 c++ dll 函数? @moez:如果你想杀死线程,你可以使用 ManualResetEvent 并与线程通信你正在中止它。也许你可以看看这个msdn.microsoft.com/en-us/library/ee191552.aspx @EugenePodskal 这是一个在 C# 线程中运行的 c++ dll 函数 【参考方案1】:

非确定性线程中止(如 Thread.Abort)是一种非常糟糕的做法。问题是,当作业不知道可以停止时,这是唯一允许您停止作业的做法。

据我所知,.NET 中没有库或框架允许编写线程代码,允许您运行任意任务并随时中止它而不会产生可怕的后果。

所以,当您决定使用某种同步技术来使用手动中止时,您完全是在写作。

解决方案

1) 最简单的方法是使用已经建议的 volatile 布尔变量:

C#

public void AbortMesh()
         
            if (currMeshStruct.Value.MeshThread != null && currMeshStruct.Value.MeshThread.IsAlive)
                            
                MeshCreator.CancelMesh();
            
        

C++/CLI

public ref class MeshCreator


    private:
        volatile System::Boolean m_StopMesh;
    ...


STDMETHODIMP MeshCreator::CancelMesh(void)

    this->m_StopMesh = TRUE;
    return S_OK;


void MeshCreator::ProcessMesh(void)

    Int32 processedParts = 0;

    while(processedParts != totalPartsToProcess)
    
        ContinueProcessing(processedParts);
        processedParts++;

        if (this->m_StopMesh)
        
            this->MakeCleanup();
            MessageBox(NULL,aMessage,L"Test",NULL);
              
    


如果您在CancelMesh 调用后不对线程的完成做出任何假设,则此类代码不需要任何同步 - 这不是即时的,可能需要不同的时间才能发生。

我不知道为什么使用 volatile 对您没有帮助,但您可以检查一下:

    您确定MeshCreator.CancelMesh(); 方法调用确实发生了吗? 您确定 m_StopMesh 在实际处理开始之前已正确初始化吗? 您确定您检查ProcessMesh 中的变量的次数足以让您的工作人员有适当的响应时间,而不是期望立即发生什么吗?

2)此外,如果您使用 .NET 4 或更高版本,您也可以尝试使用CancellationToken-CancellationTokenSource model。它最初被设计为与任务模型一起工作,但与标准线程一起工作得很好。它不会真正简化您的代码,但考虑到处理代码的异步性质可能会简化未来与TPL的集成

 CancellationTokenSource cancTokenSource = new CancellationTokenSource();
 CancellationToken cancToken = cancTokenSource.Token;

 Thread thread = new Thread(() =>
 
     Int32 iteration = 0;
     while (true)
     
         Console.WriteLine("Iteration 0", iteration);
         iteration++;
         Thread.Sleep(1000);
         if (cancToken.IsCancellationRequested)
             break;
      
  );

  thread.Start();

  Console.WriteLine("Press any key to cancel...");
  Console.ReadKey();
  cancTokenSource.Cancel();

3) 您可能想了解interlocked class、monitor locks、autoresetevents 和其他同步,但在此应用程序中实际上不需要它们

编辑:

好吧,我不知道它怎么能帮上忙(这不是best 的想法,但应该适用于这种情况),所以我稍后会尝试模拟您的应用并检查问题 -可能与 MSVC 和 CSC 处理 volatile 说明符的方式有关。

现在尝试在您的应用中使用Interlocked 读写:

public ref class MeshCreator


    private:
        System::Boolean m_StopMesh;
    ...


STDMETHODIMP MeshCreator::CancelMesh(void)

    Interlocked::Exchange(%(this->m_StopMesh), true);
    return S_OK;


void MeshCreator::ProcessMesh(void)

    Int32 processedParts = 0;

    while(processedParts != totalPartsToProcess)
    
        ContinueProcessing(processedParts);
        processedParts++;

        if (Interlocked::Read(%(this->m_StopMesh))
        
            this->MakeCleanup();
            MessageBox(NULL,aMessage,L"Test",NULL);
              
    


P.S.:您能否发布实际处理数据并检查变量的代码(我不是指您的全网格计算方法,只是它的主要阶段和元素)?

编辑:至少系统是关于什么的很清楚

您的子进程可能没有足够快地被清除。阅读this SO thread about process killing。

P.S.:并编辑您的问题以更清楚地描述您的系统和问题。对于错误或不完整的问题,很难得到正确答案。

【讨论】:

感谢您的回答,但我无法理解为什么 m_stopMesh 值仅在我显示消息框时才更改,消息框表示一种触发器或更改该值的事件。真的很奇怪 4 m 要处理取消,您必须以某种重复模式处理数据(请参阅我的 ProcessItem 函数) - 您完成了一些工作,检查停止标志,然后再次重复。 . 我唯一能想到的(如果 volatile 说明符按预期工作)是,当您认为它应该更改时,标志不会更改,而阻塞工作线程的 MessageBox 会为它成功更改提供一些时间。 我使用了您的建议,但徒劳无功,我不知道 volatile 无法解决问题 嘿,伙计,我在 cancelmesh() 中添加了一些代码,让我可以查看何时调用该方法,我发现它仅在消息框之后或在我的网格进程中运行另一个可执行文件时才被调用。有没有办法强制在我的线程中调用该方法 我对“在我的网格进程中运行另一个可执行文件”感到有点困惑,你的意思是另一个 线程 或者我对你的项目有一些非常错误的理解 - 你使用线程或进程,因为您之前说过您正在使用线程?并且当前的 Cancel 方法并非设计为在您的工作线程内调用(即 SynchronizationContext 相关功能) - 它只是设计为可以从任何线程调用并影响您的工作线程的执行。【参考方案2】:

尝试将 volatile 放在字段 m_StopMesh 之前:

volatile BOOL m_StopMesh;

【讨论】:

我把它当作 volatile bool 但徒劳无功【参考方案3】:

我使用线程启动了 c++ 进程,它运行良好。

如果您想跨进程边界进行通信,则需要使用某种跨进程通信。

http://msdn.microsoft.com/en-us/library/windows/desktop/aa365574(v=vs.85).aspx

我发现Named Pipes 方便易用。

更新

您的评论澄清了 C++ 代码正在进程内运行。

我建议ManualResetEvent。有关线程同步(和一般线程)的详细概述,请查看http://www.albahari.com/threading/

【讨论】:

感谢您澄清这一点。我根据您的评论更新了我的答案。

以上是关于检测线程内的布尔值变化的主要内容,如果未能解决你的问题,请参考以下文章

Flutter - cubit - 改变cubit内的布尔值

当表中有指定范围内的数据时返回布尔值

C ++如果一个线程写入一旦完成就会切换一个布尔值,那么在另一个线程的循环中读取该布尔值是不是安全?

*ngIf 不对布尔值变化做出反应

数据类型 数据类型转换 运算符

以编程方式检测图像是不是有边框(返回布尔值)