C# 不用lock写一个多线程程序

多线程并发

    当一段代码有可能被不止一个线程同时访问时,且存在共享资源(变量、文件句柄等),可能出现并发冲突。发生并发冲突时如果不加锁,程序的行为是不可预测的。而加锁本身又是一件麻烦事,弄不好会出现死锁,死锁时程序卡在那既没有异常也没有日志,找问题都无从下手。

    C#中通常会使用 lock 来加锁,这里应严格避免锁静态对象(如 lock(type)),避免 lock(this),这种锁的粒度都太大,容易出现死锁。

多线程程序不加锁的方法

第一,bool 类型是线程安全的,不需要锁

第二,整形, DateTime 类型可以用Interlocked

long lastTicks= DateTime.Now.Ticks;

private void multiThreadMethod()
{
    var l = Interlocked.Read(ref lastTicks);
    var lastTime = new DateTime(l);//读取上一时刻.
    //do work...

    Interlocked.Exchange(ref lastTicks, DateTime.Now.Ticks);//更新上一时刻.
}

 第三,多使用ConcurrentQuere, ConcurrentDictionary

 一个典型的数采驱动程序

    数采驱动通常需要做 接收-处理-转发 三步,为避免阻塞接收,接收后应该入队,由单独的线程完成处理、转发工作。可以设计两个类Driver, MessageHandler,Driver接受数据,MessageHandler入队处理后返回给Driver,再有Driver发送出去。

    Driver代码要关键要保持与数据源的链接,断开后能够自动重连,这部分大概率要有bool 变量在表示链接状态,有lastConnect来记录上次连接成功/数据更新的时间,以便在连接断开或数据不动多长时间后自动重连,收到到数据后入队到MessageHandler处理,并把MessageHandler返回的数据发送出去。

    下面的MessageHandler代码中有三个关键点,

  • 用ConcurrentQuere,出队/入队时避免使用lock;
  • Thread的标准写法,即有一个quitFlag 布尔变量来控制线程的退出,因为线程只有在无事可做时才能退出,不要试图从外部让线程退出;
  • Close 线程退出后,还要把队列中未处理的部分处理完。
public class MessageHandler
{
	ConcurrentQueue<string> queue;
	Thread thread;
	bool quitFlag = false;
	const int BATCH_SIZE = 100;//每次连续发送数据的数量.

	public MessageHandler()
	{
		queue = new ConcurrentQueue<string>();
	}

	public event Action<List<MyDataType>> OnDataArrive;        //有新数据.

	public void Enqueue(string msg)
	{
		queue.Enqueue(msg);
	}

	public void Open()
	{
		if (thread == null)
		{
			quitFlag = false;
			thread = new Thread(doWork);
			thread.Start();
		}
	}

	private void doWork()
	{
		while (!quitFlag)
		{
			int i = 0;
			List<MyDataType> datas = new List<MyDataType>();
			while (queue.Count > 0 && i < BATCH_SIZE)
			{
				if (queue.TryDequeue(out string msg))
				{
					doWorkBody(msg, datas);
					i++;
				}
			}
			if (datas.Count > 0)
			{
				OnDataArrive?.Invoke(datas);
			}
			Thread.Sleep(50);
		}
	}
	private void doWorkBody(string msg, List<MyDataType> datas)
	{
		try
		{
			var dataPackage = JsonConvert.DeserializeObject<DataPackage>(msg);
			foreach (var dataItem in dataPackage.Data)
			{
				datas.Add(new MyDataType()
				{
					//从MQTT payload中得到数据
				});
			}
		}
		catch(Exception ex)
		{
			Logger.Error(ex, $"解析 {msg} 出错", logSource);
		}
	}

	public void Close()
	{
		//退出线程
		quitFlag = true;
		if (thread != null)
		{
			thread.Join();
			thread = null;
		}
		List<MyDataType> datas = new List<MyDataType>();
		while (queue.Count > 0)
		{
			if (queue.TryDequeue(out string msg))
			{
				doWorkBody(msg, datas);
			}
		}//处理最后一批数据
		if (datas.Count > 0)
		{
			OnDataArrive?.Invoke(datas);
		}
	}
}

    

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 为了使用 C# 一个多线程的相机程序,您需要使用 `System.Threading` 命名空间中的 `Thread` 类。这是一个简单的例子: ```csharp using System.Threading; namespace CameraApplication { class Program { static void Main(string[] args) { // 启动一个新线程来运行相机 Thread cameraThread = new Thread(RunCamera); cameraThread.Start(); } static void RunCamera() { // 这里是相机程序的主体 // 例如,循环捕获和处理图像 } } } ``` 在这个例子中,我们在 `Main` 方法中启动了一个新线程,然后在 `RunCamera` 方法中运行相机程序的主体。您可以在 `RunCamera` 方法中添加更多的代码来捕获和处理图像。 注意:在 C# 中,如果您希望使用多线程,则必须谨慎编代码,因为多线程编程很容易出现竞争条件和死锁等问题。 ### 回答2: 使用C#多线程运行相机的程序相对比较简单,以下是一个简单的示例: ```csharp using System; using System.Threading; class CameraThread { private Thread cameraThread; private bool running; public void Start() { // 创建相机线程 cameraThread = new Thread(CameraThreadFunction); running = true; // 启动线程 cameraThread.Start(); } public void Stop() { // 停止相机线程 running = false; // 等待相机线程结束 cameraThread.Join(); } private void CameraThreadFunction() { // 相机线程主函数 while (running) { // 运行相机逻辑 Console.WriteLine("相机拍照中..."); // 模拟相机运行时间 Thread.Sleep(1000); } } } class Program { static void Main(string[] args) { // 创建相机对象 CameraThread camera = new CameraThread(); // 启动相机线程 camera.Start(); // 等待用户输入 Console.WriteLine("按任意键停止相机..."); Console.ReadKey(); // 停止相机线程 camera.Stop(); } } ``` 在这个示例程序中,我们首先定义了一个`CameraThread`类,该类封装了相机线程的操作。在该类中,我们使用`Thread`类创建了一个新的线程`cameraThread`。在`Start`方法中,我们启动了相机线程;在`Stop`方法中,我们停止了相机线程。在相机线程的主函数`CameraThreadFunction`中,我们实现了相机的运行逻辑,这里简单地打印一条消息并延迟1秒钟。在程序的主函数`Main`中,我们首先创建了一个相机对象,并使用`Start`方法启动相机线程,然后等待用户输入任意键,最后使用`Stop`方法停止相机线程。 这个示例程序只是一个简单的演示,实际的相机程序可能需要更复杂的逻辑,比如捕捉图像、进行图像处理等。但使用多线程可以提高相机程序的并发性和响应性,因此是实际开发中常用的技术。 ### 回答3: 使用C#一个多线程运行相机的程序,可以通过以下步骤实现: 1. 引入相机驱动程序库:使用C#的相机驱动库(例如AForge.NET、OpenCVSharp等)引入相机驱动程序库,以便能够与相机进行交互和控制。 2. 创建多线程:使用C#的Thread类,创建一个或多个线程来执行相机相关的操作。可以使用ThreadPool类或Task类来管理线程池。 3. 初始化相机:在主线程中初始化相机设备,设置相机参数,并将其与相机驱动程序库进行连接。 4. 图像采集线程:在一个独立的线程中,使用相机驱动程序库提供的方法来开始图像采集。 5. 图像处理线程:在另一个独立的线程中,使用相机采集到的图像进行处理。可以使用图像处理算法来检测、识别或分析图像内容。 6. 显示图像线程:在另一个独立的线程中,使用相机采集到的图像或处理后的图像,将其显示在用户界面上。 7. 线程同步:使用C#的锁(lock)机制或其他线程同步机制,确保多个线程之间的数据共享和操作的正确性。 8. 结束程序:在合适的时机,停止图像采集线程,释放相机和线程资源。 总结:通过上述方法,我们可以利用C#一个多线程运行相机的程序。使用多线程可以充分利用计算机的多核处理能力,提高图像采集和处理的效率。但是在编多线程程序时,需要注意线程同步和资源管理等问题,以避免发生线程冲突和资源泄露等情况。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值