【问题标题】:Not reach the code as expected未达到预期的代码
【发布时间】:2014-09-29 21:54:14
【问题描述】:

我有一个电话应用程序,我想在其中调用同时呼叫。每个呼叫将占用一个通道或端口。所以我将所有频道添加到BlockingCollection。该应用程序是一个 Windows 服务。

让我们看看代码。

    public static BlockingCollection<Tuple<ChannelResource, string>> bc = new BlockingCollection<Tuple<ChannelResource, string>>();
    public static List<string> list = new List<string>();// then add 100 test items to it. 

主应用有代码:

            while (true)
            {
                ThreadEvent.WaitOne(waitingTime, false);

                lock (SyncVar)
                {
                    Console.WriteLine("Block begin");
                    for (int i = 0; i < ports; i++)
                    {
                        var firstItem = list.FirstOrDefault();
                        if (bc.Count >= ports)
                            bc.CompleteAdding();
                        else
                        {
                            ChannelResource cr = OvrTelephonyServer.GetChannel();        
                            bc.TryAdd(Tuple.Create(cr, firstItem));
                            list.Remove(firstItem);
                        }
                    }

                    pc.SimultaneousCall();
                    Console.WriteLine("Blocking end");
                    if (ThreadState != State.Running) break;
                }

现在是同时调用代码:

 public void SimultaneousCall()
    {
        Console.WriteLine("There are {0} channels to be processed.", bc.Count);
        var workItemBlock = new ActionBlock<Tuple<ChannelResource, string>>(
           workItem =>
           {
               ProcessEachChannel(workItem);
           });

        foreach (var workItem in bc.GetConsumingEnumerable())
        {
            bool result = workItemBlock.SendAsync(workItem).Result;
        }

        workItemBlock.Complete();
    }

    private void ProcessEachChannel(Tuple<ChannelResource, string> workItem)
    {
        ChannelResource cr = workItem.Item1;
        string sipuri = workItem.Item2;
        VoiceResource vr = workItem.Item1.VoiceResource; 
        workItem.Item1.Disconnected += new Disconnected(workItemItem1_Disconnected);
        bool success = false;
        try
        {
            Console.WriteLine("Working on {0}", sipuri);
            DialResult dr = new DialResult();
             // blah blah for calling....
        }
        catch (Exception ex)
        {
             Console.WriteLine("Exception: {0}", ex.Message);
        }
        finally
        {
            if (cr != null && cr.VoiceResource != null)
            {
                cr.Disconnect();
                cr.Dispose();
                cr = null;
                Console.WriteLine("Release channel for item {0}.", sipuri);
            }
        }
    }

问题是当我用 4 个端口测试应用程序时,我认为代码应该到达

Console.WriteLine("Blocking end");

然而事实并非如此。请看快照。

应用程序在释放最后一个频道后刚刚挂起。我想我可能会错误地使用blockingcollection。感谢您的帮助。

更新:

即使我使用下面的POST 操作更改了代码,情况仍然没有改变。

private bool ProcessEachChannel(Tuple<ChannelResource, string> workItem)
    {
        // blah blah to return true or false respectively.
public void SimultaneousCall()
    {
        Console.WriteLine("There are {0} channels to be processed.", bc.Count);
        var workItemBlock = new ActionBlock<Tuple<ChannelResource, string>>(
           workItem =>
           {
               bool success = ProcessEachChannel(workItem);
           });

        foreach (var workItem in bc.GetConsumingEnumerable())
        {
            workItemBlock.Post(workItem);
        }

        workItemBlock.Complete();
    }

【问题讨论】:

  • @ScottChamberlain,我想返回调用结果来总结一下。说有多少失败或成功,所以我用ActionBlock.SendAsync(...).Result
  • 我错了,你错误地使用了 bc,而不是在我可以输入答案的计算机上
  • @ScottChamberlain,不着急。我可以等。
  • 如果ProcessEachChannel 是同步的,为什么不直接使用Parallel.ForEach 而不是使用所有的TPL?找出你的问题仍然很好,但对于这个实现来说这似乎更简单。
  • @MatthewHaugen,我在stackoverflow.com/questions/22688679/… 有旧线程。一条评论指出“如果消费者继续工作并将东西添加到队列中,这将不起作用”由 oleksii。

标签: c# task-parallel-library async-await .net-4.5


【解决方案1】:

我认为问题在于您从不调用bc.CompleteAdding()if 意味着它将在循环的第ports + 1 次迭代中调用,但循环仅迭代ports-次。因此,GetConsumingEnumerable() 返回一个永不结束的序列,这意味着SimultaneousCall() 中的foreach 永远阻塞。

我认为正确的解决方案是在for 循环之后调用bc.CompleteAdding() ,而不是在其中不可能的情况。

【讨论】:

  • @Love,对不起,我昨天没能回答,但这个答案几乎涵盖了我要说的内容。
  • 我可以丢弃ActionBlock 代码吗?就用一个简单的,foreach (var workItem in bc.GetConsumingEnumerable()) { bool success = ProcessEachChannel(workItem); } 我也想清空blockingcollection。
  • @Love 是的,您当然可以这样做,尽管这意味着您的代码会表现不同:使用ActionBlock,您无需等待处理完成。
猜你喜欢
  • 1970-01-01
  • 2018-12-23
  • 2023-03-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多