Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/299.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何在.NET中通过串行端口异步传输?_C#_.net_Serial Port - Fatal编程技术网

C# 如何在.NET中通过串行端口异步传输?

C# 如何在.NET中通过串行端口异步传输?,c#,.net,serial-port,C#,.net,Serial Port,我正在编写一个程序来模拟通过串行端口传输数据的设备。为此,我在表单中创建了一个System.IO.Ports.SerialPort对象,并创建了一个System.Windows.Forms.Timer以指定频率进行传输。一切正常,除了当频率接近串行端口速度限制时,它开始锁定UI,并最终在发送数据以进行传输时变得无响应,传输速度超过端口数据速度。我的代码是: private void OnSendTimerTick(object sender, EventArgs e) { StringB

我正在编写一个程序来模拟通过串行端口传输数据的设备。为此,我在表单中创建了一个System.IO.Ports.SerialPort对象,并创建了一个System.Windows.Forms.Timer以指定频率进行传输。一切正常,除了当频率接近串行端口速度限制时,它开始锁定UI,并最终在发送数据以进行传输时变得无响应,传输速度超过端口数据速度。我的代码是:

private void OnSendTimerTick(object sender, EventArgs e)
{
    StringBuilder outputString = new StringBuilder("$", 51);
    //code to build the first output string
    SendingPort.WriteLine(outputString.ToString());
    outputString = new StringBuilder("$", 44);
    //code to build the second output string
    SendingPort.WriteLine(outputString.ToString());

    if (SendingPort.BytesToWrite > 100)
    {
        OnStartStopClicked(sender, e);
        MessageBox.Show("Warning: Sending buffer is overflowing!");
    }
}
我希望WriteLine函数是异步的——当端口在后台传输时立即返回。相反,OnSendTimerTick函数似乎是正确线程化的,但WriteLine似乎是在UI线程中运行的


如何使串行端口以这种方式工作?在计时器中创建SerialPort对象似乎不是一个好主意,因为这样我就必须在每次计时器计时时打开和关闭它。

它只是部分异步的,它将立即返回,但只要您写入的字节适合串行端口驱动程序的传输缓冲区。当你用过多的数据淹没端口时,它会停止尖叫,比它传输的速度快。您可以使用SerialPort.BaseStream.BeginWrite()方法使其真正异步。这不会使它更快,但会将瓶颈移到其他地方,可能会远离UI。

如果您创建了一个要写入的字符串队列,并且有一个后台线程从队列写入串行端口,则可以解决这一问题(UI的慢度)。如果采用这种方法,请注意队列的大小


编辑:由于某些原因,我不能使用添加注释,所以我只编辑这个。BeginWrite的文档中有这样一句话:“流上BeginWrite的默认实现会同步调用Write方法,这意味着Write可能会阻塞某些流。”然后它继续排除文件流和网络流,但不排除SerialPort。我想您可以试试看。

如果您使用的是
System.Windows.Forms.Timer
,则计时器事件处理程序将在UI线程上执行。当
SynchronizingObject
设置为表单时,如果使用
System.Timers.Timer
,情况也是如此。如果串行端口缓冲区已满,线程必须等待,直到有足够的空间容纳要发送的新数据

我建议您使用
System.Threading.Timer
来完成此操作。在池线程上调用计时器回调,这意味着如果
WriteLine
必须等待,UI线程将不会锁定。如果执行此操作,则必须确保在任何时候只有一个线程执行计时器回调。否则,您可能会使数据混乱。最好的方法是将计时器设为一次性,并在每次回调结束时重新初始化:

const int TimerFrequency = 50;  // or whatever
System.Threading.Timer timer;

void InitTimer()
{
    timer = new System.Threading.Timer(TimerCallback, null, TimerFrequency, Timeout.Infinite);
}

void TimerCallback(object state)
{
    // do your stuff here
    // Now reset the timer
    timer.Change(TimerFrequency, Timeout.Infinite);
}
传递有效的
Timeout.Infinite
作为
period
参数可防止计时器成为周期计时器。相反,它只发射一次。每次发送后,
Timer.Change
会重新初始化计时器


处理此问题的一个可能更好的方法是通过将
WriteBufferSize
设置为足够大的值来完全消除计时器。然后,您的程序可以将其所有数据转储到缓冲区中,并让
SerialPort
实例担心将数据通过线路运出。当然,这假设您可以创建一个足够大的缓冲区,以容纳您的程序试图发送的任何内容。

这是有道理的,但如果它是这样工作的,那么为什么在我检查SendingPort.BytesToWrite时,它没有检测到缓冲区的填充速度快于传输速度?因为您是在写入之后而不是之前检查它的?不太确定,如果相关的话,USB模拟器的驱动程序往往是不稳定的。我在这个问题上不够清楚,但被模拟的设备只是以给定的频率连续发送几个参数的更新。队列没有意义,因为如果传输速率快于端口速率,它就会无限增长。这就是我尝试检查缓冲区大小的原因——如果缓冲区增长,那么速率太快,发送器应该抛出错误并停止。