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

本文关键字:C++ 外部 例程 任务 何取消 取消 执行 | 更新日期: 2023-09-27 18:28:30

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

如果用户委托调用外部非托管C++例程,是否有方法使用创建时传递给Task的Cancellation Token来取消Task?

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

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

从一个非托管的外部C++例程可以做到这一点吗?

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

以下是我试图修复的混合C#/C++Cli/非托管C++代码的示例(摘录),以便能够取消用户委托在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 ?
        }
    }
}

谢谢你的帮助。

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

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

我要做的是声明您的非托管方法使用指向布尔值的指针,并在布尔值设置为true时使非托管代码中止。在包装中,使用CancellationToken.Register注册回调,该回调将在CancellationToken被取消时将布尔值设置为true。

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

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是指向布尔的指针,所以它在堆上。因此,我们可以将指针传递给它,而无需执行任何特殊操作(例如,固定托管对象)
  • CancellationTokenRegistration实现了IDisposable,当reg超出范围时,它将自动注销自己。(您可以使用C#中的using语句来完成此操作。)

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