同时运行不正确,提供比预期更多的频道

时间:2014-10-13 13:23:36

标签: c# asynchronous task-parallel-library async-await tpl-dataflow

我有一个应用程序,可以同时运行任务。我们在这里设定 MaxDegreeOfParallelism=4,这意味着在任何时候最多同时运行4个任务。在这种情况下,我只有4个频道可用。否则是异常

  

无法获得频道

将被抛出。

每个任务都有一个OutboundDial的实例,所以最多只有4个实例。

 public class OutboundDial
 {
    private ChannelResource m_ChannelResource;
    private VoiceResource m_VoiceResource;
    private TelephonyServer m_TelephonyServer;
    private AppointmentReminderResult m_Result = new AppointmentReminderResult();

    public OutboundDial(TelephonyServer telephonyServer)
    {
        m_TelephonyServer = telephonyServer;
    }

    internal void RunScript(AppointmentReminder callData) 
    {
        try
        {
            try
            {
                m_ChannelResource = m_TelephonyServer.GetChannel();
                m_VoiceResource = m_ChannelResource.VoiceResource;
            }
            catch (Exception ex)
            {
                Console.WriteLine("Could not get channel: {0}",ex.StackTrace);
                return;
            }
            // a long running process of I/O bound operation

生产者 - 消费者队列

 public static BufferBlock<AppointmentReminder> m_Queue =
        new BufferBlock<AppointmentReminder>(new ExecutionDataflowBlockOptions() { MaxDegreeOfParallelism = 4});

BufferBlock是TPL类。 TelephontServer最初初始化。

public static TelephonyServer ts;
ts = new TelephonyServer(sIpaddress, "username", "password");

在消费者部分,我们有:

static async  Task Consumer()
{
    try
    {
        while (await m_Queue.OutputAvailableAsync())
        {
            m_Queue.TryReceive(4, ts); // MaxDegreeOfParallelism = 4
        }
    }

TryReceive是一种扩展方法。

 public static void TryReceive<T>(this BufferBlock<T> bufferBlock, int count, TelephonyServer ts) where T : AppointmentReminder
    {
        try
        {
            for (var i = 0; i < count; i++)
            {
                T item;
                if (bufferBlock.TryReceive(out item))
                {

                    Task t = Task.Run(() =>
                        {
                            OutboundDial d = new OutboundDial(ts);
                            d.RunScript<T>((T)item);
                        });
                }
                else
                {
                    break;
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.StackTrace);
        }
    }

我的问题:我在生产者部分的队列中添加了10个项目,并在构造函数中设置了一个断点。

我发现代码在构造函数中运行了10次,然后运行了10次RunScript,这表示10个任务一起运行而不是4个。但我只想要4(MaxDegreeOfParallelism)。因此,我没有足够的频道可用,抛出异常。

为什么在我的扩展方法中并发运行不起作用?

2 个答案:

答案 0 :(得分:1)

  1. BufferBlock并未真正执行任何操作,因此指定其MaxDegreeOfParallelism没有意义。它的作用只是因为ExecutionDataflowBlockOptions继承自DataflowBlockOptions,这是BufferBlock构造函数所期望的。

  2. 你的Consumer()执行此操作:最多需要4个项目并执行它们,最多需要4个项目并执行它们,最多需要4项并执行它们等等。因为你永远不会等待那些要完成的执行,你实际上并没有以这种方式限制并行度。

  3. 如果您想将并行度限制为4,则可以使用ActionBlock,而不是BufferBlockConsumer()的组合:

    new ActionBlock<AppointmentReminder>(
        reminder => new OutboundDial(ts).RunScript(reminder),
        new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 4});
    

    这样做是为每个提醒执行lambda,但最多同时执行4个,这似乎是你要求的。虽然我不确定你需要什么,因为你似乎不会在使用后释放(处置)频道,用于下一次提醒。

答案 1 :(得分:0)

目前还不清楚问题出在哪里,但似乎不是在方法之后调用cinstructor。 我建议你将构造函数代码更改为:

public OutboundDial(TelephonyServer telephonyServer)
{
    m_TelephonyServer = telephonyServer;
    Console.WriteLine(m_Telephonyserver);
}

然后你会确定构造函数是完整的。

此外,在RunScript中的每一行之后添加一些Console.WriteLine和有用的信息 - 然后您将看到错误的来源。