2014-10-30 3 views
4

비 관리 C++ 루틴으로 작성된 외부 DLL에서 수행되는 취소 가능한 작업을 시작하는 C# 비동기 코드를 수정하려고합니다.관리되지 않는 C++ 외부 루틴을 실행하는 작업을 취소하는 방법

사용자 대리인이 관리되지 않는 외부 C++ 루틴을 호출하는 경우 생성시 작업에 전달 된 취소 토큰을 사용하여 작업을 취소하는 방법이 있습니까?

내가 알다시피, 작업 취소에는 사용자 대리인과 취소를 요청한 코드 간의 협력이 필요합니다. 성공적인 취소에는 CancellationTokenSource.Cancel 메서드를 호출하는 요청 코드와 사용자가 취소 요청이 제기 된 것을 알았을 때 대리자에서 단순히 반환하여 (CancellationToken.IsCancellationRequested를 폴링하여) 작업을시기 적절하게 종료하는 대리자가 필요합니다. 메서드)를 사용하거나 CancellationToken.ThrowIfCancellationRequested 메서드를 사용하여 OperationCanceledException을 throw합니다. (http://msdn.microsoft.com/en-us/library/dd997396%28v=vs.110%29.aspx)

이 두 가지 방법은 CancellationToken을 매개 변수로 받고 일정한 간격으로 IsCancellationRequested 및/또는 ThrowIfCancellationRequested 메서드를 호출하여 사용자 대리자에 의해 실행되는 관리되지 않는 C++ 루틴이 협조하는 방식입니다.

관리되지 않는 외부 C++ 루틴에서 그렇게 할 수 있습니까?

그렇지 않은 경우 요청 코드에 의해 취소가 요청 될 때 사용자 위임을 실행하는 작업이 강제 종료되는 방법 (비 관리 C++ 루틴 실행)이 있습니까?

다음은 C++ 비 관리 코드 파트에서 사용자 대리인에 의해 수행 된 작업을 취소 할 수 있도록 해결하려는 혼합 C#/C++ Cli/비 관리 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 ? 

     } 
    } 
} 

감사합니다.

답변

3

순수하게 관리되지 않는 코드를 다루는 경우 CancellationToken 클래스에 대해 알지 못하므로 관리되는 코드 에서처럼 코드를 전달할 수 없습니다.

내가 할 것은 관리되지 않는 메서드를 선언하여 부울로 포인터를 가져오고 부울을 true로 설정하면 관리되지 않는 코드가 중단되게하는 것입니다. 래퍼에서 CancellationToken.Register를 사용하여 CancellationToken이 취소 될 때 Boolean을 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 있기 때문에 힙에있어, 부울에 대한 포인터입니다. 따라서 특별한 작업 (예 : 관리 객체 고정) 없이도 포인터를 전달할 수 있습니다.
  • CancellationTokenRegistrationIDisposable을 구현하면 reg이 범위를 벗어날 때 자동으로 등록이 해제됩니다. (C#의 using 문을 사용하면됩니다.)

면책 조항 : 저는 지금 컴파일러가 아닙니다. 구문 오류가있을 수 있습니다.

관련 문제