gpt4 book ai didi

C#定时信号量同步两个线程和单个输出缓冲区

转载 作者:太空宇宙 更新时间:2023-11-03 23:33:35 24 4
gpt4 key购买 nike

添加前言

在这里我想更好地解释一下我的应用场景。

我需要一个 Windows 服务来将 SerialPort“转换”为 TCPPort。例如,假设我有一个串行票据打印机连接到原始 ascii 流的 COM 端口,我想通过网络的 TCP 套接字访问它。结果应该是串行打印机变成网络打印机,我的服务应该将许多 tcp 套接字链接到 com 端口。

这是方案: enter image description here

主要问题是 COM 端口有一个唯一的连接,但在这里我可以有许多来自网络客户端的同时连接。我需要同步写入 COMport 并从 COMport 获取输出并将其复制到所有连接的 TCP 客户端。

使用 TCP 连接时,我无法知道写入流何时真正关闭,因为网络客户端可以在不关闭其连接的情况下发送打印作业,并在一段时间后发送另一个作业。串行打印机是联机打印机,没有开始/结束命令,它可以简单地接收 ascii 字符,它们是按接收顺序排列的打印机。这是因为我需要确保网络输入不会混合,并且我想要一个计时器,它可以在释放同步写锁之前了解作业是否真的结束。


原始问题

我有两个线程:AB

两个线程都必须通过 WriteToOutput() 方法写入单个输出缓冲区,我想确保如果 AB 输出不会混合> 想同时写入输出。首先我需要一个简单的信号量:

private object locker = new object();

public void WriteToOutput(byte[] threadBuffer)
{
lock (locker)
{
//... copy threadBuffer to outputBuffer
}
}

但我需要更安全地划分输出,因为线程可以清空其缓冲区,但它可以在锁释放后立即填充。

所以在并发的情况下,如果线程 A 获得了锁,我想等待第二个线程 B 一段时间,比方说 1 秒。如果此时线程A要写更多的东西,它有优先权,B必须再等一个tick。如果线程A没有写n个完整的tick,那么它真的可以释放锁,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 .

关于C#定时信号量同步两个线程和单个输出缓冲区,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31321929/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com