如何将SynchronizationContext与WCF一起使用

zim*_*nen 8 wcf synchronizationcontext

我正在阅读SynchronizationContext并尝试确保OperationContext即使在await通话之后我试图将所有线程都流入所有线程也不会弄乱任何东西.

我有这SynchronizationContext门课:

public class OperationContextSynchronizationContext : SynchronizationContext
{

    // Track the context to make sure that it flows through to the next thread.

    private readonly OperationContext _context;

    public OperationContextSynchronizationContext(OperationContext context)
    {
        _context = context;
    }

    public override void Post(SendOrPostCallback d, object state)
    {
        OperationContext.Current = _context;
        d(state);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后围绕每个方法调用(使用a Ninject IInterceptor)调用它:

var original = SynchronizationContext.Current;
try
{
    // Make sure that the OperationContext flows across to the other threads,
    // since we need it for ContextStack.  (And also it's cool to have it.)
    SynchronizationContext.SetSynchronizationContext(new OperationContextSynchronizationContext(OperationContext.Current));

    // Process the method being called.
    invocation.Proceed();
}
finally
{
    SynchronizationContext.SetSynchronizationContext(original);
}
Run Code Online (Sandbox Code Playgroud)

它似乎工作(我可以根据需要使用OperationContext),但这是正确的方法吗?我错过了一些可能会在以后咬我的重要事项吗?

编辑 Stephen Cleary的一些评论:

public class OperationContextSynchronizationContext : SynchronizationContext, IDisposable
{

    // Track the context to make sure that it flows through to the next thread.

    private readonly OperationContext _context;
    private readonly SynchronizationContext _previous;

    public OperationContextSynchronizationContext(OperationContext context)
    {
        _context = context;
        _previous = SynchronizationContext.Current;
        SynchronizationContext.SetSynchronizationContext(this);
    }

    public override void Post(SendOrPostCallback d, object state)
    {
        OperationContext.Current = _context;
        d(state);
        //(_previous ?? new SynchronizationContext()).Post(d, state);
    }

    private bool _disposed = false;
    public void Dispose()
    {
        if (!_disposed)
        {
            SynchronizationContext.SetSynchronizationContext(_previous);
            _disposed = true;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

最终:

public class OperationContextSynchronizationContext : SynchronizationContext, IDisposable
{

    // Track the operation context to make sure that it flows through to the next call context.

    private readonly OperationContext _context;
    private readonly SynchronizationContext _previous;

    public OperationContextSynchronizationContext()
    {
        _context = OperationContext.Current;
        _previous = SynchronizationContext.Current;
        SynchronizationContext.SetSynchronizationContext(this);
    }

    public override void Post(SendOrPostCallback d, object state)
    {
        var context = _previous ?? new SynchronizationContext();
        context.Post(
            s =>
            {
                OperationContext.Current = _context;
                try
                {
                    d(s);
                }
                catch (Exception ex)
                {
                    // If we didn't have this, async void would be bad news bears.
                    // Since async void is "fire and forget," they happen separate
                    // from the main call stack.  We're logging this separately so
                    // that they don't affect the main call (and it just makes sense).

                    // log here
                }
            },
            state
        );
    }

    private bool _disposed = false;
    public void Dispose()
    {
        if (!_disposed)
        {
            // Return to the previous context.
            SynchronizationContext.SetSynchronizationContext(_previous);
            _disposed = true;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Ste*_*ary 2

有几件事对我来说很突出。

SynchronizationContext首先,我不建议为此使用 a 。您正在尝试使用框架解决方案解决应用程序问题。会起作用的;我只是从架构的角度来看这是有问题的。不过,唯一的替代方案并不那么干净:可能最合适的方法是编写一个扩展方法,Task返回一个保留OperationContext.

其次,实现OperationContextSynchronizationContext.Post直接执行委托。这有几个问题:一方面,委托应该异步执行(我怀疑 .NET 框架或 TPL 中的一些地方假设了这一点)。另一方面,这SynchronizationContext有一个具体的实现;在我看来,如果自定义 SyncCtx 包装现有的 SyncCtx 会更好。一些 SyncCtx 有特定的线程要求,现在OperationContextSynchronizationContext正在充当这些要求的替代品而不是补充

第三,自定义SyncCtx在调用其委托时不会将自身设置为当前SyncCtx。await因此,如果同一个方法中有两个 s,它将不起作用。