gpt4 book ai didi

c# - 如何取消执行非托管 C++ 外部例程的任务

转载 作者:太空狗 更新时间:2023-10-30 01:18:36 31 4
gpt4 key购买 nike

我正在尝试修复 C# 异步代码启动在非托管 C++ 例程中编写的外部 dll 中执行的可取消操作。

如果用户委托(delegate)调用外部非托管 C++ 例程,是否可以使用在创建时传递给任务的取消 token 来取消任务?

据我所知,任务取消涉及用户委托(delegate)和请求取消的代码之间的合作。成功的取消涉及调用 CancellationTokenSource.Cancel 方法的请求代码,以及用户委托(delegate)及时终止操作,方法是在委托(delegate)注意到已提出取消请求时简单地返回(通过轮询 CancellationToken.IsCancellationRequested方法)或使用 CancellationToken.ThrowIfCancellationRequested 方法抛出 OperationCanceledException。 (参见 http://msdn.microsoft.com/en-us/library/dd997396%28v=vs.110%29.aspx)

这两种方式涉及由用户委托(delegate)执行的非托管 C++ 例程通过接收 CancellationToken 作为参数并定期调用其 IsCancellationRequested 和/或 ThrowIfCancellationRequested 方法进行协作。

是否可以从非托管的外部 C++ 例程中做到这一点?

如果不是,有没有办法在请求代码请求取消时强制终止执行用户委托(delegate)(执行非托管 c++ 例程)的任务?

这是我试图修复的混合 C#/C++Cli/非托管 C++ 代码的示例(摘录),以便能够取消用户委托(delegate)在 C++ 非托管代码部分中执行的操作:

FrmDemo.cs:---------------------------------------- ------------------------------

public class FrmDemo : Form
{
private CliClass m_CliObject;
private System.Threading.CancellationTokenSource m_Cts;
private System.Threading.CancellationToken m_Ct;

private void FrmDemo_Load(object sender, EventArgs e)
{
// Creating the external CliObject:
this.m_CliObject = new NSDemo.CliClass();
...
}

// Event handler of the button starting the cancelable asynchrone operation:
private async void btnStart_Click(object sender, EventArgs e)
{
m_Cts = new System.Threading.CancellationTokenSource();
m_Ct = m_Cts.Token;
await Task.Factory.StartNew(() =>
{
// Launching a cancelable operation performed by a managed C++Cli Object :
this.m_CliObject.DoSomething(); // How to eventually pass the CancellationToken m_ct to the m_CliObject ?
}, m_ct);
...
}


//Event handler of the cancel button:
private void btnCancel_Click(object sender, EventArgs e)
{
// Requesting cancellation:
m_Cts.Cancel();
// (Or alternatively, how to eventually force the termination of the async Task without collaboration from it ?)
}

CliClass.h:---------------------------------------- ----------

#include "DemoCore.h"

using namespace System;
using namespace System::Runtime::InteropServices;
using namespace cli;

namespace NSDemo
{
public ref class CliClass
{

public:

CliClass();

~CliClass();

void DoSomething()
{
// Performing the operation in the unmanaged coreObject:
_coreObject->DoSomething();
}

private:
UNSDemo::CoreClass *_coreObject;
bool _disposed;

};
}

CliClass.cpp:----------------------------------------

namespace NSDemo
{
CliClass::CliClass()
{
_coreObject = new UNSDemo::CoreClass(...);
....
}

CliClass::~CliClass()
{
if (_disposed)
return;
if (_coreObject != nullptr) {
delete _coreObject;
_coreObject = nullptr;
}
_disposed = true;
GC::SuppressFinalize(this);
}

CoreClass.h-----------------------------------------------------------------

namespace UNSDemo {

class __declspec(dllexport) CoreClass {
public:
ScanningCore();

~ScanningCore();

void DoSomething();

private:

...

};

}

CoreClass.cpp:---------------------------------------- ----------------------------------

#include "CoreClass.h"

namespace UNSDemo {

CoreClass::CoreClass()
{
...
}

CoreClass::~CoreClass()
{
...
}

// Method actually performing the cancelable operation:
void CoreClass::DoSomething()
{
// Main loop of the unmanaged cancelable operation:
while (...) {
...
// How to check the cancellation request from here ? (How to access the CancellationToken ?)
// and if cancellation is requested, how to eventually throw the OperationCanceledException ?

}
}
}

感谢您的帮助。

最佳答案

如果您处理的是纯非托管代码,它不知道 CancellationToken 类,因此您不能像处理托管代码那样传递它。

我要做的是声明您的非托管方法以获取指向 bool 值的指针,并在 bool 值设置为真时让非托管代码自行中止。在您的包装器中,使用 CancellationToken.Register 注册一个回调,该回调将在取消 CancellationToken 时将 bool 值设置为 true。

这在表面上听起来很简单,但它有点复杂,因为您需要一个托管事件处理程序来访问您可以获取其地址的 bool 值。

public ref class CancelableTaskWrapper
{
private:
bool* isCanceled;
void (*unmanagedFunctionPointer)(bool*);

void Canceled() { if (isCanceled != nullptr) *isCanceled = true; }

public:
CancelableTaskWrapper(void (*unmanagedFunctionPointer)(bool*))
{
this->unmanagedFunctionPointer = unmanagedFunctionPointer;

isCanceled = new bool;
}

~CancelableTaskWrapper() { if (isCanceled != nullptr) delete isCanceled; isCanceled = nullptr; }
!CancelableTaskWrapper() { if (isCanceled != nullptr) delete isCanceled; isCanceled = nullptr; }

void RunTask(CancellationToken cancelToken)
{
*isCanceled = false;
CancellationTokenRegistration reg = cancelToken.Register(
gcnew Action(this, &CancelableTaskWrapper::Canceled));
unmanagedFunctionPointer(isCanceled);
}
};

void someUnmanagedFunction(bool* isCanceled)
{
doSomethingLongRunning();
if(*isCanceled) return;
doSomethingLongRunning();
}
  • 因为 isCanceled 是指向 bool 值的指针,所以它在堆上。因此,我们可以将指针传递给它而无需执行任何特殊操作(例如,固定托管对象)。
  • CancellationTokenRegistration 实现了 IDisposable,当 reg 超出范围时,它会自动取消注册。 (您可以使用 C# 中的 using 语句执行此操作。)

免责声明:我现在不在编译器;可能有语法错误。

关于c# - 如何取消执行非托管 C++ 外部例程的任务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26654609/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com