C#-周期性数据读取和线程.Sleep()
我的C#应用程序从特殊的USB设备读取数据。数据被读取为所谓的“消息”,每个消息有24个字节。每秒必须读取的消息量可能不同(最大频率相当高,约为每秒700条消息),但应用程序必须全部读取 读取消息的唯一方法是调用函数“ReadMessage”,该函数返回从设备读取的一条消息。该函数来自外部DLL,我无法修改它 我的解决方案是:我有一个单独的线程,在程序运行期间一直在运行,它唯一的任务就是循环读取消息。然后在主应用程序线程中处理接收到的消息 在“读取线程”中执行的功能如下:C#-周期性数据读取和线程.Sleep(),c#,multithreading,C#,Multithreading,我的C#应用程序从特殊的USB设备读取数据。数据被读取为所谓的“消息”,每个消息有24个字节。每秒必须读取的消息量可能不同(最大频率相当高,约为每秒700条消息),但应用程序必须全部读取 读取消息的唯一方法是调用函数“ReadMessage”,该函数返回从设备读取的一条消息。该函数来自外部DLL,我无法修改它 我的解决方案是:我有一个单独的线程,在程序运行期间一直在运行,它唯一的任务就是循环读取消息。然后在主应用程序线程中处理接收到的消息 在“读取线程”中执行的功能如下: private voi
private void ReadingThreadFunction() {
int cycleCount;
try {
while (this.keepReceivingMessages) {
cycleCount++;
TRxMsg receivedMessage;
ReadMessage(devHandle, out receivedMessage);
//...do something with the message...
}
}
catch {
//... catch exception if reading failed...
}
}
此解决方案工作正常,所有消息都能正确接收。然而,应用程序消耗了太多的资源,我的计算机的CPU运行率超过80%。因此我想减少它
多亏了“cycleCount”变量,我知道线程的“循环速度”大约是每秒40000个循环。这是不必要的太多,因为我需要接收最高700消息/秒。(而且该设备具有大约100条消息的缓冲区,因此循环速度甚至可以稍低一些)
我试图通过线程暂停线程1ms来降低循环速度;指挥部。当然,这不起作用,循环速度变为大约70个循环/秒,不足以读取所有消息。我知道这种尝试是愚蠢的,让线程进入睡眠状态,然后叫醒他需要比1毫秒长得多的时间
但是,我不知道还能做什么:除了thread.Sleep之外,还有其他方法可以降低线程执行速度(以减少CPU消耗)吗?或者我完全错了,我应该为这个任务使用不同的东西,而不是线程,可能是线程。计时器或线程池
非常感谢您的建议。这是我在这里的第一个问题,我是使用线程的初学者,如果不够清楚,请原谅。我建议使用一个分析器,或者使用VS2010中的分析器。您需要确定瓶颈,而不是随意地在这里或那里更改一些东西,看看它是否会突然起作用。如果没有消息,ReadMessage会返回什么 只有当线程告诉您没有消息时,您才应该休眠线程。在这种情况下,只需睡1毫秒就可以了。我会:
//...do something with the message...
// If the message is null, it indicates that we've read everything there is to be read.
// So lets sleep for 1ms to give the buffer chance to receive a few extra messages.
If (theMessage == null) // Buffer is empty as we failed to read a new message
{
Thread.Sleep(1);
}
这种方式可能会给您带来最大的性能,而不是重击CPU。线程。睡眠也意味着放弃线程的时间片。只有在再次调度线程后,才能返回到该线程。这就是为什么你的“睡眠”时间超过1毫秒。另见 问题似乎是您必须主动轮询DLL以获取新消息。更好的解决方案是让DLL在新消息到达时通知您。但是,您说过不能修改外部DLL 你可以尝试的是降低成本 编辑: 例如,您可以使用一个大约每毫秒发射一次。当事件尚未触发时,Dispatchermer不会重击您的CPU。在处理程序中,您可以处理消息(我假设您可以判断当前是否没有消息),直到没有消息可以处理为止。您还必须防止重入,即在事件处理程序已经运行时进入事件处理程序 作为代码示例:
// Creating the DispatcherTimer
var dispatcherTimer = new System.Windows.Threading.DispatcherTimer();
dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
dispatcherTimer.Interval = TimeSpan.FromMilliseconds(1);
dispatcherTimer.Start();
private void dispatcherTimer_Tick(object sender, EventArgs e)
{
if (this.ProcessingEvents)
return;
this.ProcessingEvents = true;
var messagesLeft = true;
while (messagesLeft)
{
TRxMsg receivedMessage;
ReadMessage(devHandle, out receivedMessage);
// Process message
// Set messagesLeft to false if no messages left
}
this.ProcessingEvents = false;
}
使用超时为1毫秒的AutoResetEvent怎么样
private void ReadingThreadFunction() {
try {
var autoReset = new AutoResetEvent(false);
autoReset.WaitOne(1);
TRxMsg receivedMessage;
ReadMessage(devHandle, out receivedMessage);
//...do something with the message...
}
}
catch {
//... catch exception if reading failed...
}
}
编辑:一定要像其他答案建议的那样检查消息是否可用…使用高精度计时器。你应该尝试使用多媒体定时器。它将帮助您降低CPU使用率。我不确定是否会重新着色,但对于一个恒定的时间(如果不不断更改时间间隔,则相当准确)。您可以保持10毫秒的分辨率,并且对于每次调用计时器,您可以读取整个缓冲区,直到其为空或100次。这应该会有所帮助。您可以尝试: SpinWait本质上是将处理器放入一个非常紧密的循环中,循环计数由iterations参数指定。因此,等待的持续时间取决于处理器的速度 将此与睡眠方法进行对比。调用Sleep的线程将产生其当前处理器时间片的剩余部分,即使指定的间隔为零。为睡眠指定非零间隔将使线程调度程序不再考虑该线程,直到时间间隔结束 SpinWait被设计为在不产生当前时间片的情况下等待
它是为那些你知道你想在很短的时间内完成某件事情的情况而设计的,因此失去时间片将是多余的。我认为你应该做的是使用一个带计时器的单例类。这个类的作用是每秒提供700条消息,如果需要更多的时间,它将每秒等待和重试700条消息。您可以在另一个线程上实例化它。singleton是线程安全的
// Queue Start
ProcessQueue.Instance.Start();
// Queue Stop
ProcessQueue.Instance.Stop();
例如:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Timers;
using System.Diagnostics;
namespace Service.Queue
{
/// <summary>
/// Process Queue Singleton Class
/// </summary>
class ProcessQueue : IDisposable
{
private static readonly ProcessQueue _instance = new ProcessQueue();
private bool _IsProcessing = false;
int _maxMessages = 700;
public bool IsProcessing
{
get { return _IsProcessing; }
set { _IsProcessing = value; }
}
~ProcessQueue()
{
Dispose(false);
}
public static ProcessQueue Instance
{
get
{
return _instance;
}
}
/// <summary>
/// Timer for Process Queue
/// </summary>
Timer timer = new Timer();
/// <summary>
/// Starts Process Queue
/// </summary>
public void Start()
{
timer.Elapsed += new ElapsedEventHandler(OnProcessEvent);
timer.Interval = 1000;
timer.Enabled = true;
}
/// <summary>
/// Stops Process Queue
/// </summary>
public void Stop() {
_IsProcessing = false;
timer.Enabled = false;
}
/// <summary>
/// Process Event Handler
/// /// </summary>
private void OnProcessEvent(object source, ElapsedEventArgs e)
{
if (!_IsProcessing)
{
_IsProcessing = true;
for (int i = 0; i < _maxMessages; i++)
{
TRxMsg receivedMessage;
ReadMessage(devHandle, out receivedMessage);
}
_IsProcessing = false;
}
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
timer.Dispose();
}
}
#region IDisposable Members
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
}
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
使用系统文本;
使用系统计时器;
使用系统诊断;
命名空间服务.队列
{
///
///进程队列单例类
///
类ProcessQueue:IDisposable
{
私有静态只读ProcessQueue_实例=新ProcessQueue();
私有bool_IsProcessing=false;
int_maxMessages=700;
公共布尔处理
{
获取{return\u IsProcessing;}
设置{u IsProcessing=value;}
}
~ProcessQueue()
{
处置(虚假);
}
公共静态ProcessQueue实例
{
得到
{
返回_实例;
}
}
///
///计时器