1

我正在更新与 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 中的标签。

回顾一下,上面的第 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() 的情况下,它通过 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 最终会再次响应,但这显然不应该发生。

4

1 回答 1

4

You're calling BeginInvoke for every message that comes in. That ends up queue-ing a whole bunch of UI updates that happen one after another, effectively blocking the UI thread.

You'd be better off calling Invoke for each message. Or, even better, changing your design so that you empty the queue and do a single UI update with all of the messages in one shot.

于 2013-08-02T19:32:38.313 回答