c# - NetTcpBinding 和异步/等待 WCF 阻塞

标签 c# wcf asynchronous async-await nettcpbinding

我们正在创建一个共享的 WCF channel 以用于异步操作:

var channelFactory = new ChannelFactory<IWcfService>(new NetTcpBinding {TransferMode = TransferMode.Buffered});

channelFactory.Endpoint.Behaviors.Add(new DispatcherSynchronizationBehavior(true, 25));
var channel = channelFactory.CreateChannel(new EndpointAddress(new Uri("net.tcp://localhost:80/Service").AbsoluteUri + "/Test"));

这会调用以下服务:

[ServiceContract]
public interface IWcfService
{
    [OperationContract]
    Task<MyClass> DoSomethingAsync();
}

[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.PerCall)]
public class WcfServiceImpl : IWcfService
{
    public Task<MyClass> DoSomethingAsync()
    {
        Thread.Sleep(4000);

        return Task.FromResult(new MyClass());
    }
}

[Serializable]
public class MyClass
{
    public string SomeString { get; set; }      
    public MyClass Related { get; set; }        
    public int[] Numbers { get; set; }
}

如果我们一次启动 3 个请求并在响应上模拟一个长时间运行的任务:

        using ((IDisposable)channel)
        {
            var task1 = Task.Run(async () => await DoStuffAsync(channel));
            var task2 = Task.Run(async () => await DoStuffAsync(channel));
            var task3 = Task.Run(async () => await DoStuffAsync(channel));

            Task.WaitAll(task1, task2, task3);
        }
    }

    public static async Task DoStuffAsync(IWcfService channel)
    {
        await channel.DoSomethingAsync();

        Console.WriteLine("Response");

        // Simulate long running CPU bound operation
        Thread.Sleep(5000);

        Console.WriteLine("Wait completed");
    }

然后所有 3 个请求同时到达服务器,然后服务器同时响应所有 3 个请求。

然而,一旦响应到达客户端,它就会依次处理每一个。

Response
// 5 second delay
Wait completed
// Instant
Response
// 5 second delay
Wait completed
// Instant
Response

响应在不同的线程上恢复,但每次只运行 1。

如果我们使用流而不是缓冲,我们会得到预期的行为,客户端同时处理所有 3 个响应。

我们已尝试设置最大缓冲区大小、使用 DispatcherSynchronizationBehaviour、不同的并发模式、切换 session 、ConfigureAwait false 并调用 channel.Open() 明确地。

似乎无法在共享 session 上获得正确的并发响应。

编辑

我已经添加了我认为正在发生的事情的图像,这只发生在缓冲模式下,在流模式下主线程不会阻塞。

3 threads spawn concurrently but responses are received in series for buffered mode

最佳答案

@下划线

我最近试图解决完全相同的问题。虽然,在使用它的线程被释放之前,我无法确切地确定为什么 TransferMode.Buffered 会导致 WCF channel 上的全局锁定,但我发现了类似的情况问题 deadlock after awaiting .他们提出了一种解决方法,即将 RunContinuationsAsynchronously() 添加到您的等待中,即 await channel.DoSomethingAsync().RunContinuationsAsynchronously() 其中 RunContinuationsAsynchronously() :

public static class TaskExtensions
{
    public static Task<T> RunContinuationsAsynchronously<T>(this Task<T> task)
    {
        var tcs = new TaskCompletionSource<T>();

        task.ContinueWith((t, o) =>
        {
            if (t.IsFaulted)
            {
                if (t.Exception != null) tcs.SetException(t.Exception.InnerExceptions);
            }
            else if (t.IsCanceled)
            {
                tcs.SetCanceled();
            }
            else
            {
                tcs.SetResult(t.Result);
            }
        }, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);

        return tcs.Task;
    }

    public static Task RunContinuationsAsynchronously(this Task task)
    {
        var tcs = new TaskCompletionSource<object>();

        task.ContinueWith((t, o) =>
        {
            if (t.IsFaulted)
            {
                if (t.Exception != null) tcs.SetException(t.Exception.InnerExceptions);
            }
            else if (t.IsCanceled)
            {
                tcs.SetCanceled();
            }
            else
            {
                tcs.SetResult(null);
            }
        }, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);

        return tcs.Task;
    }
}

它分隔 WCF 延续。显然 Task.Yield() 也有效。

不过,如果能真正理解为什么会发生这种情况,那就太好了。

关于c# - NetTcpBinding 和异步/等待 WCF 阻塞,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48772506/

相关文章:

c# - 我应该接受同一个对象作为参数并返回它还是只接受它作为参数?

c# - 如何分隔文本文件每一行中的字符串并将它们与用户输入进行比较

c# - 存储对字符串的引用

c# - 如何检测 CI 构建中的 WCF 重大更改

javascript - React类组件中的同步Meteor调用

javascript - 变量不保存 ionic Angular 数据

c# - 列出 Google Drive api 中的文件会导致系统格式异常

.net - WCF 服务库的用途是什么?

.net - 如何在 Fiddler2 中拦截服务到服务的调用?

javascript - 如何处理异步ajax响应