添加了前缀
在这里,我想更好地解释我的应用程序的场景。
我需要Windows服务来将“串行端口”“转换”为TCPPort。例如,假设我有一个串行票据打印机连接到用于原始ascii流的COM端口,并且我想通过TCP套接字从网络访问它。结果应该是串行打印机成为网络打印机,我的服务应该将许多tcp套接字链接到com端口。
这是方案:

主要问题是COM端口具有唯一的连接,但是在这里我可以从网络客户端获得许多同时连接。我需要同步对COMport的写入并从COMport获取输出,并将其复制到所有连接的TCP客户端。
使用TCPconnections,我不知道何时真正关闭写流,因为网络客户端可以发送打印作业而无需关闭其连接,并在一段时间后发送另一个作业。
串行打印机是嵌入式打印机,没有开始/结束命令,它可以简单地接收ascii字符,并且它们按接收顺序是打印机。
这是因为我需要确保不会混入网络输入,并且需要一个计时器,该计时器可以在释放同步的写锁定之前了解作业实际上已经结束。

原始问题
我有两个线程: A B
两个线程都必须通过WriteToOutput()方法写入单个输出缓冲区,并且我想确保如果 A B 都想同时写入输出,则不会混合输出。
首先,我需要一个简单的信号量:

private object locker = new object();

public void WriteToOutput(byte[] threadBuffer)
{
    lock (locker)
    {
        //... copy threadBuffer to outputBuffer
    }
}
但是我需要更多的安全性来划分输出,因为线程可以清空其缓冲区,但是可以在锁定释放后立即将其填充。
因此,在并发的情况下,如果线程 A 获得了锁定,我想等待第二个线程 B 一段时间,我们将其打勾1s。如果在这个时间线程 A 要写更多内容,则它具有优先级,而 B 必须等待另一个滴答。如果线程 A 没有写完整的n个滴答声,那么它实际上可以释放该锁,并且 B 线程可以获取该锁。

最佳答案

仅用于校正-这是监视器,而不是信号灯。

至于其余的,这听起来像是一个怪异的多线程设计,而且将变得脆弱且不可靠。在安全释放共享资源的时候使它变得显而易见-依靠任何类型的定时进行同步都是一个糟糕的主意。

问题在于,WriteToOutput方法显然不是同步的好方法!如果需要确保对来自同一线程的多次写入进行序列化,则需要将同步点移动到其他位置。或者,传递一个Stream而不是byte[],并读取该信息,直到它在锁中关闭为止-这将有效地执行相同的操作,将责任移交给被调用方。只要确保您不会忘记关闭流就永远不会将其锁定:)另一个选择是使用BlockingCollection<byte[]>。当我们真的不知道您实际上要做什么时,很难说出什么是最佳选择。

编辑:

好的,串行端口通信是我能想到的唯一正确使用时序的方法。当然,在非实时系统上处理通信也可能有些棘手。

解决此问题的最佳方法是为所有对串行端口的访问提供单个端点,以处理通信和同步。无需从其他线程调用该方法,只需发布​​端点将读取的数据。但是,这需要您有一种识别其他线程的方法-我不确定您是否有类似的东西(也许是TCP套接字的EndPoint?)。最简单的方法是使用BlockingCollection:

private readonly object _syncObject = new object();
public void SendData(BlockingCollection<byte[]> data)
{
  lock (_syncObject)
  {
    byte[] buffer;

    while (data.TryTake(out buffer, TimeSpan.FromSeconds(1)))
    {
      // Send the data
    }
  }
}

只要它将在最多一秒钟的时间内获取另一个缓冲区,这将继续从队列中读取和发送数据-如果花费的时间超过一秒钟,则该方法将退出并且另一个线程将有机会。

在套接字接收线程中,您将声明阻塞集合-这将根据接收代码的实现而有所不同。如果对于每个不同的套接字只有某个类的单个实例,则只需将其声明为实例字段即可。如果没有,您可以使用ThreadLocal。假设您使用的是手动线程,每个插槽一个-如果没有,则需要一个不同的存储空间。
private readonly BlockingCollection<byte[]> _dataQueue = new BlockingCollection<byte[]>();

private void ReceiveHandler(byte[] data)
{
  // This assumes the byte array passed is already a copy
  _data.Add(data);
  SendData(_dataQueue);
}

这绝对不是解决此问题的最佳方法,但肯定是我现在能想到的最简单的方法-几乎没有任何代码,并且仅使用lockBlockingCollection

09-10 08:08
查看更多