WinForms UI有时没有响应

本文关键字:响应 WinForms UI | 更新日期: 2023-09-27 18:25:56

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

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

此功能划分如下:

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

  2. 将消息上传到web服务的代码作为一个任务运行,该任务由计时器以15秒的间隔启动。该代码只需将所有SMS消息排成队列,上传到web服务,更新UI中的标签以指示队列中没有消息,然后终止。

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

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

要重新设置上限,请将上面的项目2、3和4作为任务运行。我将相同的取消令牌传递到每个任务中,以便在用户关闭我的应用程序时取消它们。除了以下情况外,一切似乎都按预期进行:如果GSM调制解调器正在运行,但我的应用程序没有运行,则传入的SMS消息将存储在SIM卡上,直到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()的情况下,它通过queues.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()的大量异步尝试有关?UI最终会再次响应,但这显然不应该发生。

WinForms UI有时没有响应

您正在为传入的每一条消息调用BeginInvoke。这最终会使一大堆相继发生的UI更新排队,从而有效地阻塞UI线程。

您最好为每条消息致电Invoke。或者,更好的是,更改您的设计,以便清空队列,并在一次拍摄中对所有消息进行单个UI更新。