我正在更新一个WinForms应用程序,该应用程序与GSM调制解调器通信,简而言之,执行以下功能:
- 接受传入的SMS消息并将其上载到基于SOAP的web服务
- 从基于SOAP的web服务下载消息,将其转换为SMS消息并发送到目标地址
此功能划分如下:
-
处理传入SMS消息的代码是GSM库的一部分。它被设置为一个事件处理程序,当调制解调器接收到新的传入消息时会触发。事件处理程序只是将消息转换为特定的字符串格式(源电话号码、消息正文),并将其添加到队列中以上载到web服务。
-
将消息上传到web服务的代码作为一个任务运行,该任务由计时器以15秒的间隔启动。该代码只需将所有SMS消息排成队列,上传到web服务,更新UI中的标签以指示队列中没有消息,然后终止。
-
从web服务下载消息的代码也作为任务运行,它也由间隔15秒的计时器触发。它只需从web服务下载任何新消息,将它们添加到固定大小的"传出"队列中,更新UI中的标签以指示队列中的消息数量,然后终止。
-
最后,构造和发送队列中传出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最终会再次响应,但这显然不应该发生。
您正在为传入的每一条消息调用BeginInvoke
。这最终会使一大堆相继发生的UI更新排队,从而有效地阻塞UI线程。
您最好为每条消息致电Invoke
。或者,更好的是,更改您的设计,以便清空队列,并在一次拍摄中对所有消息进行单个UI更新。