WinForms UI有时无响应

时间:2013-08-02 19:26:19

标签: c# multithreading winforms task-parallel-library

我正在更新与GSM调制解调器通信的WinForms应用程序,简而言之,执行以下功能:

  • 接受传入的SMS消息并将其上载到基于SOAP的Web服务
  • 从基于SOAP的Web服务下载消息,将其转换为SMS消息并将其发送到目标地址

此功能分为:

  1. 处理传入SMS消息的代码是GSM库的一部分。它被设置为事件处理程序,当调制解调器收到新的传入消息时将触发该事件处理程序。事件处理程序只是将消息转换为特定的字符串格式(源电话号码,消息正文),并将其添加到队列以上载到Web服务。

  2. 将消息上载到Web服务的代码作为任务运行,该任务由具有15秒间隔的计时器启动。此代码简单地将所有SMS消息出列,将它们上载到Web服务,更新UI中的标签以指示队列中没有消息然后终止。

  3. 从Web服务下载邮件的代码也作为任务运行,它也由具有15秒间隔的计时器触发。它只是从Web服务下载任何新消息,将它们添加到固定大小的“传出”队列,更新UI中的标签以指示队列中的消息数,然后终止。

  4. 最后,构造和发送队列中的传出SMS消息的代码作为任务运行。此代码在一个相当紧密的循环中运行,当传出队列中有任何消息时,它会不断发送消息。此代码还会在从队列中删除邮件并将其发送到目标电话号码后更新UI中的标签。

  5. 要重新上限,上面的第2,3和4项作为任务运行。我将相同的取消令牌传递给每个任务,以便在用户关闭我的应用程序时取消它们。除了以下情况外,一切似乎都按预期工作:如果GSM调制解调器正在运行但我的应用程序没有运行,传入的SMS消息将存储在SIM卡上,直到它已满(在这种情况下为30条消息),之后,它们将开始在SMSC排队。当我的应用程序启动时,我会检查存储在SIM卡上的消息并进行处理。如果SIM已满,则只要我开始处理并删除这些消息,就会开始在SMSC排队的任何消息。在这一点上,有时我的UI会冻结。我基本上有三段代码可以进行UI更新,它们被我上面描述的各种任务调用:

        // update the status textbox (a multi-line textbox)
        private void UpdateStatusText(string text)
        {
            textBoxStatus.BeginInvoke((MethodInvoker)delegate { textBoxStatus.AppendText(text + Environment.NewLine); });
        }
    
        // update UI with the current number of messages in the incoming queue
        private void UpdateIncomingMessageQueueCount()
        {
            labelIncomingQueueCounter.BeginInvoke((MethodInvoker)delegate { labelIncomingQueueCounter.Text = messagesFromDevicesQueue.GetCount().ToString(); });
        }
    
        // update the UI with the current number of messages in the outgoing queue
        private void UpdateOutgoingMessageQueueCount()
        {
            labelOutgoingQueueCounter.BeginInvoke((MethodInvoker)delegate { labelOutgoingQueueCounter.Text = messagesToDevicesQueue.GetCount().ToString(); });
        }
    

    我不完全确定是什么导致冻结发生但是当它发生并且我能够在调试器中断开时,我可以看到一些状态为“已阻止”且悬停在提示上的任务说“没有等待信息“。正如我之前所说,只有当有大量传入消息时才会发生这种情况。在这种情况下,我正在调用UpdateIncomingMessageQueueCount()一个相当数量(在每个传入的消息入队后)。在UpdateIncomingMessageQueueCount()的情况下,它通过队列.GetCount()方法获取队列中的消息数。固定大小的队列对象实现如下:

    public class FixedSizeQueue<T>
    {
    
    private readonly List<T> queue = new List<T>();
    private readonly object syncObj = new object();
    
    public int Size { get; private set; }
    
    public FixedSizeQueue(int size)
    {
        Size = size;
    }
    
    public void Enqueue(T obj)
    {
        lock (syncObj)
        {
            queue.Insert(0, obj);
    
            if (queue.Count > Size)
            {
                queue.RemoveRange(Size, queue.Count - Size);
            }
        }
    }
    
    public T[] DequeueAllItems()
    {
        lock (syncObj)
        {
            var result = queue.ToArray();
            queue.Clear();
            return result;
        }
    }
    
    public void RemoveFirstItem()
    {
        lock (syncObj)
        {
            if (queue.Count > 0)
            {
                queue.RemoveAt(0);
            }
        }
    }
    
    public T Peek()
    {
        lock (syncObj)
        {
            if (queue.Count > 0)
            {
                var result = queue[0];
                return result;
            }
            else
            {
                return default(T);
            }
        }
    }
    
    public void Flush()
    {
        lock (syncObj)
        {
            queue.Clear();
        }
    }
    
    public int GetCount()
    {
        lock (syncObj)
        {
            return queue.Count;
        }
    }
    }
    

    由于我正在使用.BeginInvoke()更新UI,我想知道我的性能问题是否与大量异步尝试在队列对象上调用.GetCount()有关?用户界面最终会再次响应,但显然不应该发生这种情况。

1 个答案:

答案 0 :(得分:4)

您正在为每条收到的邮件调用BeginInvoke。这最终会导致一堆接一组的UI更新排队,从而有效地阻止了UI线程。

最好为每封邮件调用Invoke。或者,更好的是,更改您的设计,以便清空队列并一次性完成所有消息的单个UI更新。