如何跨AppDomain边界传递CancellationToken?

gap*_*gap 14 .net c# concurrency appdomain abort

我有一个命令对象,根据请求队列的请求进行工作.此特定命令将在子appdomain中执行其工作.在子appdomain中完成其工作的一部分涉及阻止ConcurrentQueue操作(例如,Add或Take).我需要能够通过请求队列传播中止信号,传递到子appdomain,并唤醒其中的工作线程.

因此,我认为我需要在AppDomain边界传递CancellationToken.

我尝试创建一个继承自MarshalByRefObject的类:

protected class InterAppDomainAbort : MarshalByRefObject, IAbortControl
    {
        public InterAppDomainAbort(CancellationToken t)
        {
            Token = t;
        }

        [SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.Infrastructure)]
        public override object InitializeLifetimeService()
        {
            return null;
        }

        public CancellationToken Token
        {
            get;
            private set;
        }

    };
Run Code Online (Sandbox Code Playgroud)

并将此作为参数传递给worker函数:

// cts is an instance variable which can be triggered by another thread in parent appdomain
cts = new CancellationTokenSource();
InterAppDomainAbort abortFlag = new InterAppDomainAbort(cts.Token);
objectInRemoteAppDomain = childDomain.CreateInstanceAndUnwrap(...);

// this call will block for a long while the work is being performed.
objectInRemoteAppDomain.DoWork(abortFlag);
Run Code Online (Sandbox Code Playgroud)

但是当objectInRemoteAppDomain尝试访问Token getter属性时,我仍然会遇到异常:

System.Runtime.Serialization.SerializationException: Type 'System.Threading.CancellationToken' in Assembly 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' is not marked as serializable.
Run Code Online (Sandbox Code Playgroud)

我的问题是:如何在appdomains中传播中止/取消信号并唤醒可能在.NET并发数据结构中被阻止的线程(支持CancellationToken参数).

sha*_*tor 22

自从我查看任何跨AppDomain的东西已经有一段时间了,所以这段代码可能存在问题我还没有意识到,但它似乎可以完成这项工作.根本问题是似乎无法将CancellationToken [Source]从一个AppDomain转移到另一个AppDomain.所以我创建了两个源,主要设置为在适当时取消辅助.

在这种情况下两个单独的令牌源这一事实当然可能是一个问题,但我认为你没有解决这样一个事实,即缺乏可序列性会阻止你在两个单独的AppDomain中使用相同的令牌.

关于最小错误检查,Dispose实现等的标准警告

// I split this into a separate interface simply to make the boundary between
// canceller and cancellee explicit, similar to CancellationTokenSource itself.
public interface ITokenSource
{
    CancellationToken Token { get; }
}

public class InterAppDomainCancellable: MarshalByRefObject,
                                        ITokenSource,
                                        IDisposable
{
    public InterAppDomainCancellable()
    {
        cts = new CancellationTokenSource();
    }

    public void Cancel() { cts.Cancel(); }

    // Explicitly implemented to make it less tempting to call Token
    // from the wrong side of the boundary.
    CancellationToken ITokenSource.Token { get { return cts.Token; } }

    public void Dispose() { cts.Dispose(); }

    private readonly CancellationTokenSource cts;
}

// ...

// Crucial difference here is that the remotable cancellation source
// also lives in the other domain.
interAppDomainCancellable = childDomain.CreateInstanceAndUnwrap(...);

var primaryCts = new CancellationTokenSource();
// Cancel the secondary when the primary is cancelled.
// CancellationToken.Register returns a disposable object which unregisters when disposed.
using (primaryCts.Token.Register(() => interAppDomainCancellable.Cancel()))
{
    objectInRemoteAppDomain = childDomain.CreateInstanceAndUnwrap(...);
    // DoWork expects an instance of ITokenSource.
    // It can access Token because they're all in the same domain together.
    objectInRemoteAppDomain.DoWork(interAppDomainCancellable);
    // ... some other work which might cancel the primary token.
}
Run Code Online (Sandbox Code Playgroud)

  • 谢谢。Token.Register通过在worker对象上实现cancel方法帮助我解决了该问题,因此我不需要InterAppDomainCancellable类。小笔记:也许配置由Register函数返回的CancellationTokenRegistration,请参见:https://docs.microsoft.com/zh-cn/dotnet/api/system.threading.cancellationtoken.register?view=netframework-4.7.1不知道是否会给您带来麻烦,但是我想它没有理由没有实现IDispose ;-) (2认同)