Not reach the code as expected

2019-08-02 20:12发布

问题:

I have a telephony application, in which I want to invoke simultaneous calls,. Each call will occupy a channel or port. So I added all channels to a BlockingCollection. The application is a windows service.

Let's see the code.

    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. 

The main application has the code:

            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;
                }

Now for the simultaneous call code:

 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);
            }
        }
    }

The question was when I tested the application with 4 ports, I thought the code should reach at

Console.WriteLine("Blocking end");

However it was not. Please see the snapshot.

The application is just hanging on after releasing the last channel. I guess that I may use the blockingcollection incorrectly. Thanks for help.

UPDATE:

Even I changed the code by using POST action as below, the situation is still unchanged.

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();
    }

回答1:

I believe the problem is that you never call bc.CompleteAdding(): the if means it would be called in ports + 1-th iteration of the loop, but the loop iterates only ports-times. Because of this, GetConsumingEnumerable() returns a sequence that never ends, which means the foreach inside SimultaneousCall() blocks forever.

I think the right solution is to call bc.CompleteAdding() after the for loop, not in an impossible condition inside it.