C#WPF与WinForm使用多线程调用硬件设备且不会卡顿界面的操作方法(保证线程安全)

1.背景:有30台TCP设备需要同时建立套接字,并且需要一键对它们进行各种操作(传统单线程方法会导致界面长时间卡住,不能点击,且不会有实时信息返回)。

2.如何创建一个新的线程,与使用委托的方法直接传参与调用函数:

Thread thr = new Thread(())//创建单个线程
thr.Start();//启动

Thread n = new Thread(new ThreadStart(() => MyCode(a)));//使用委托并传递参数的写法
Thread n = new Thread(() => MyCode(a));//此为上行代码的简写

3.建立多个插座就是在全局先声明,等传入IP端口等数据后再赋值给它们,下面简单写个例子:

Socket sc1;
Socket sc2;

private void Button连接_Click(object sender, RoutedEventArgs e)
        {
            string sum = TBIP1.Text.Trim() + "," + TBIP2.Text.Trim();
            string[] arr = sum.Split(',');
            String IP1 = arr[0];
            String IP2 = arr[1];
            Thread thr1 = new Thread(() => Con(1, IP1));
            Thread thr2 = new Thread(() => Con(2, IP2));
            thr1.Start(); thr2.Start(); 
        }

private void Con(int ID, string IP)
        {
            if (IP != "")
            {
                try
                {
                    switch (ID)
                    {
                        case 1:
                            sc1 = ScanCtrl.Connect(IP, 4001, 1000);
                            bool flag1 = sc1.Connected;//根据设备返回值判断是否连接成功
                            if (flag1)
                            {
                                Conputsuc(ID);
                            }
                            else
                            {
                                Conputfalse(ID);
                            }
                            break;
            //剩余部分省略了
}}}}

设备是否连接成功肯定是要提供反馈的,如果在上面代码的ConPutSuc(ID);处写对界面的操作的话会跨线程,如AppendText通过,会不允许操作,因为规定界面的操作必须在主线程完成,解决方案在第4条。

4.把操作转回到主线程,也就是我的ConPutSuc(ID);方法,使用Dispatcher.Invoke(new Action(()=> {//你的代码})):

private void Conputsuc(int ID)
        {
            Dispatcher.Invoke(new Action(() =>
            {
                TB信息.AppendText(ID + "号设备,连接成功!" + DateTime.Now + "\r\n");
                TB信息.ScrollToEnd();
            }));
        }

如果是winform应用,是没有Dispatchar.Invoke的,可以使用BeginInvoke。

5.如果两个线程都操作了我的精读函数,肯定会导致输出顺序是随机的,或者有可能直接导致内部数据错乱,有很多种方法,这里就先忽略性能和线程独立,使用一种简单的方法来处理,就是给多线程操作的这一块内存或者说方法加锁,方法如下:

private object MyLock = new object();//先在外部创建一个锁对象

private void Con(int ID, string IP)
        {
            lock (MyLock)
            {
                //需要锁住的代码
            }
        }


这样锁住之后这块代码一次只能被一个线程操作,其它的先排队。

C# 中实现延时执行的方法或函数,主要目的是在不阻塞主线程的前提下完成延迟操作。以下是几种常见的实现方式,适用于不同的应用场景。 ### 使用 `System.Timers.Timer` 实现延迟执行 该方法通过创建一个定时器对象,在指定时间间隔后触发事件并执行目标操作。适用于一次性任务的延迟执行: ```csharp public static void SetTimeOut(double interval, Action action) { System.Timers.Timer timer = new System.Timers.Timer(interval); timer.Elapsed += (sender, e) => { timer.Enabled = false; action(); }; timer.AutoReset = false; timer.Enabled = true; } ``` 此方法的优点是简单易用,不会阻塞主线程,适合用于后台任务的延迟执行[^1]。 --- ### 使用线程池和 `BeginInvoke` 实现 UI 延迟更新 当需要在 WinForm 应用中执行耗时操作并更新 UI 时,可以使用线程池来避免界面冻结问题: ```csharp if (MessageBox.Show("确定要清理吗?", "确认", MessageBoxButtons.YesNo) == DialogResult.Yes) { this.labMsg.Text = "正在清理,请稍候..."; ThreadPool.QueueUserWorkItem(state => { Thread.Sleep(2000); // 延迟两秒执行 BeginInvoke(new Action(() => { string result = Clean(); this.labMsg.Text = result; })); }); } ``` 这种方法利用了多线程处理耗时任务,并通过 `BeginInvoke` 将结果安全地返回到 UI 线程进行更新[^2]。 --- ### 自定义精确延时函数 对于需要更精确控制延迟时间的场景,可以编写自定义的延时函数: ```csharp public bool Delay(int ms) { DateTime now = DateTime.Now; int s; do { TimeSpan spand = DateTime.Now - now; s = spand.Minutes * 60 * 1000 + spand.Seconds * 1000 + spand.Milliseconds; Application.DoEvents(); } while (s < ms); return true; } ``` 该方法通过不断检查经过的时间来确保准确的延迟效果,并通过 `Application.DoEvents()` 允许其他消息继续处理,从而保持程序响应性[^3]。 --- ### 非独占性延时实现 在 WPF 应用中,可以通过以下方式实现非独占性延时,使界面仍然能够响应用户输入: ```csharp public static void Delay(int milliSecond) { int start = Environment.TickCount; while (Math.Abs(Environment.TickCount - start) < milliSecond) { Application.DoEvents(); // 处理其他操作 } } ``` 这种方式通过循环检测时间差并调用 `Application.DoEvents()` 来持续处理其他消息,从而避免界面卡顿[^4]。 --- 综上所述,C# 提供了多种实现延时执行的方法,开发者可以根据具体的应用场景选择合适的方案。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值