我有一个c ++ dll函数,我想在C#线程中运行。
有些时候我需要取消该线程,这就是问题所在:
Thread.Abort()在我读过的众多文章中都是邪恶的 主题
唯一的方法是使用bool并定期检查它的值。
我的问题是,即使我将此值设置为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()
,这里的值也总是假的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;
答案 0 :(得分:3)
非确定性线程堕胎(与Thread.Abort一样)是一种非常糟糕的做法。问题是,当工作不知道可以停止工作时,这是唯一允许您停止工作的做法。
我知道.NET中没有库或框架允许编写线程代码,允许您运行任意任务并随时中止它而不会产生可怕的后果。
所以,当你决定使用一些同步技术手动中止时,你就完全写了。
<强>解决方案强>:
1)最简单的一个是使用已经建议的易变的布尔变量:
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();
方法调用确实发生了吗?ProcessMesh
内的变量,以便从您的工作人员那里获得不错的响应时间,而不是期待瞬间发生的事情吗?2)此外,如果您使用.NET 4或更高版本,您也可以尝试使用CancellationToken-CancellationTokenSource model。它最初设计用于处理Tasks模型,但适用于标准线程。它不会真正简化您的代码,但考虑到处理代码的异步性质可能会简化将来与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。:并编辑您的问题以更清楚地描述您的系统和问题。对于错误或不完整的问题,很难得到正确的答案。
答案 1 :(得分:1)
我使用线程启动了c ++进程并且运行正常。
如果要跨进程边界进行通信,则需要使用某种跨进程通信。
http://msdn.microsoft.com/en-us/library/windows/desktop/aa365574(v=vs.85).aspx
我发现Named Pipes方便易用。
<强>更新强>
您的评论澄清了C ++代码正在进行中运行。
我建议ManualResetEvent。有关线程同步(以及一般的线程)的精彩概述,请查看http://www.albahari.com/threading/
答案 2 :(得分:1)
尝试在字段m_StopMesh:
之前放置volatilevolatile BOOL m_StopMesh;