WCF双工内的TPL数据流阻塞

时间:2013-03-21 12:31:10

标签: c# .net wcf task-parallel-library tpl-dataflow

我是SO的新作家,请耐心等待。

我有一个带双工服务合同的WCF服务。该服务合同具有操作联系人,该联系人假设进行长数据处理。我被限制为限制并发数据处理的数量让我们说max 3.我的问题是在数据处理之后我需要回到相同的服务实例上下文,所以我回调我的启动器端点传递数据处理结果。我需要提一下,由于各种原因,我受限于TPL数据流和WCF双工。

这是我到目前为止所写内容的演示

在控制台库中,我模拟WCF调用

class Program
{
    static void Main(string[] args)
    {
        // simulate service calls

        Enumerable.Range(0, 5).ToList().ForEach(x =>
        {
            new System.Threading.Thread(new ThreadStart(async () =>
            {
                var service = new Service();
                await service.Inc(x);
            })).Start();
        });
    }
}

这就是假设是WCF服务

// service contract
public class Service
{
    static TransformBlock<Message<int>, Message<int>> transformBlock;

    static Service()
    {
        transformBlock = new TransformBlock<Message<int>, Message<int>>(x => Inc(x), new ExecutionDataflowBlockOptions
        {
            MaxDegreeOfParallelism = 3
        });
    }

    static Message<int> Inc(Message<int> input)
    {
        System.Threading.Thread.Sleep(100);

        return new Message<int> { Token = input.Token, Data = input.Data + 1 };
    }

    // operation contract
    public async Task Inc(int id)
    {
        var token = Guid.NewGuid().ToString();

        transformBlock.Post(new Message<int> { Token = token, Data = id });

        while (await transformBlock.OutputAvailableAsync())
        {
            Message<int> message;
            if (transformBlock.TryReceive(m => m.Token == token, out message))
            {
                // do further processing using initiator service instance members
                // something like Callback.IncResult(m.Data);
                break;
            }
        }
    }
}

public class Message<T>
{
    public string Token { get; set; }

    public T Data { get; set; }
}

操作合同不一定非同步,但我需要OutputAvailableAsync通知。

这是一个好方法还是我的方案有更好的解决方案?

提前致谢。

1 个答案:

答案 0 :(得分:1)

首先,我认为你不应该像你那样使用令牌。在进程间进行通信时,唯一标识符很有用但是当你进入一个进程时,只需使用引用相等。

要真正回答你的问题,我认为(某种)繁忙的循环不是一个好主意。

更简单的异步限制解决方案是使用SemaphoreSlim。类似的东西:

static readonly SemaphoreSlim Semaphore = new SemaphoreSlim(3);

// operation contract
public async Task Inc(int id)
{
    await Semaphore.WaitAsync();

    try
    {
        Thread.Sleep(100);
        var result = id + 1;
        // do further processing using initiator service instance members
        // something like Callback.IncResult(result);
    }
    finally
    {
        Semaphore.Release();
    }
}

如果您真的想(或必须?)使用数据流,可以使用TaskCompletionSource在操作和块之间进行同步。操作方法将等待Task的{​​{1}},并且块在完成该消息的计算时将设置它:

TaskCompletionSource