C# 让程序代码在固定的线程里运行

news2025/2/1 3:57:23

一、概述

在平时我们的开发中,多线程也是经常用到的,尤其是我们做上位机行业的,平时更是必不可少,在以前我做 Unity3d 开发时,其实并不用关心线程的问题,在 Unity 的开发中,所有代码基本都是单线程运行,而且还可以保持比较高的运行速度,当然,这不是本次要讨论的话题。

有人可能会问我这么做的意义,系统自动分配线程不是更好么?当然好,只是有时候调用其他的一些框架,就避免不了需要锁定线程,比如,C# 调用 C++ 的 DLL,最近在做发那科的上位机程序,在调用 fanuc 的一些方法时,需要传入一个句柄,在测试中,我发现如果使用多线程,切换到了其他的线程根本无法行的通,返回数据都是报错,但是在主程序中,虽然可以正常运行,但是一但执行了写入G代码这类接口时,整个程序全部卡死,这就不得不用多线程,并且必须让指定的代码,在指定的程序中运行。

二、实现功能

新建一个 winform 项目,界面中就两个按钮,用来测试多线程的影响

新建一个类 MessagePump,当前类的功能就是开启一个线程,加入一个任务队列,并重复的检测在任务队列中有没有可以执行的任务。

其实当类还可以写的更复杂,比如,自定义一个任务系统,可以传入任务的名字,任务的委托,和回调的委托,任务需要的一些参数等,这里我就不具体去写啦,有需求的可以自己试试。

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

internal class MessagePump
{
    private static bool m_Working = false;
    private static Queue<Action> m_Actions = new Queue<Action>();

    public static void Start()
    {
        if (m_Working)
            return;

        m_Working = true;
        Thread t = new Thread(DoPump);
        t.Name = "Message Pump Thread";
        t.Start();
    }

    private static void DoPump()
    {
        while (m_Working)
        {
            try
            {
                Monitor.Enter(m_Actions);
                while (m_Actions.Count > 0)
                {
                    Console.WriteLine("------start------");
                    m_Actions.Dequeue()();
                    Console.WriteLine("------end------");
                }
            }
            finally
            {
                Monitor.Exit(m_Actions);
            }

            Thread.Sleep(500);
        }
    }

    public static void Stop()
    {
        m_Working = false;
    }

    public static void AddMessage(Action act)
    {
        Task.Run(() =>
        {
            lock (m_Actions)
            {
                m_Actions.Enqueue(act);
            }
        });
    }
}

Form1 窗体的代码

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace 多线程
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            #region 注释
            //var c = new System.Collections.Concurrent.BlockingCollection<Tuple<bool, Action>>();
            //var t = new Thread(() =>
            //{
            //    while (true)
            //    {
            //        var item = c.Take();
            //        if (!item.Item1) break;
            //        item.Item2();
            //    }
            //    Console.WriteLine("Exiting thread");
            //});
            //t.Start();

            //Console.WriteLine("Press any key to queue first action");
            //Console.ReadKey();
            //c.Add(Tuple.Create<bool, Action>(true, () => Console.WriteLine("Executing first action")));

            //Console.WriteLine("Press any key to queue second action");
            //Console.ReadKey();
            //c.Add(Tuple.Create<bool, Action>(true, () => Console.WriteLine("Executing second action")));

            //Console.WriteLine("Press any key to stop the thread");
            //Console.ReadKey();
            //c.Add(Tuple.Create<bool, Action>(false, null));

            //Console.WriteLine("Press any key to exit");

            #endregion

            MessagePump.Start();


            MessagePump.AddMessage(() =>
            {
                string threadid = Thread.CurrentThread.ManagedThreadId.ToString();
                Console.WriteLine("-------------任务{0}当前的线程ID:{1}", 1, threadid);

                Thread.Sleep(1000);

                Console.WriteLine("-------------任务1完成-------------");
            });

            MessagePump.AddMessage(() =>
            {
                string threadid = Thread.CurrentThread.ManagedThreadId.ToString();
                Console.WriteLine("-------------任务{0}当前的线程ID:{1}", 1, threadid);

                int value = 0;
                while (true)
                {
                    Thread.Sleep(1000);
                    value++;
                    if (value > 10)
                    {
                        break;
                    }
                }
                Console.WriteLine("-------------任务2完成-------------");
            });

            MessagePump.AddMessage(() =>
            {
                string threadid = Thread.CurrentThread.ManagedThreadId.ToString();
                Console.WriteLine("-------------任务{0}当前的线程ID:{1}", 3, threadid);

                Thread.Sleep(3000);

                Console.WriteLine("-------------任务3完成-------------");
            });
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            MessagePump.Stop();
        }

        //使用异步打开定时器
        private void button1_Click(object sender, EventArgs e)
        {
            isStop = true;
            DosomeThingAsync();
        }

        private static bool isStop = false;
        private static async void DosomeThingAsync()
        {
            while (isStop)
            {
                await Task.Delay(TimeSpan.FromSeconds(2));

                string threadid = Thread.CurrentThread.ManagedThreadId.ToString();
                Console.WriteLine("定时器--线程ID:" + threadid);
            }
        }

        //新添加一个任务
        private void button2_Click(object sender, EventArgs e)
        {
            MessagePump.AddMessage(() =>
            {
                string threadid = Thread.CurrentThread.ManagedThreadId.ToString();
                Console.WriteLine("-------------任务{0}当前的线程ID:{1}", 5, threadid);

                Thread.Sleep(5000);

                Console.WriteLine("-------------任务{0}完成-------------", 5);
            });
        }
    }
}

窗体在运行后,会自动添加任务,并执行,还任务还没执行完成时,我们点击 “使用异步打开定时器” 这个按钮,临时切换一些线程,看看之前添加的线程会不会改变线程ID

点击 “新添加一个任务” 按钮,可以看到,线程的ID并没有变,这样,我们锁定线程去执行代码的功能就实现了。

所有的代码都在这里了,源码我就不上传了。 

三、SynchronizationContext

提供在各种同步模型中传播同步上下文的基本功能。

类 SynchronizationContext 是提供不同步的自由线程上下文的基类。

此类实现的同步模型的目的是允许公共语言运行时的内部异步/同步操作在不同的同步模型中正常运行。 此模型还简化了托管应用程序为了在不同的同步环境中正常工作而必须遵循的一些要求。

同步模型的提供程序可以扩展此类,并为这些方法提供自己的实现。

上面是微软的一些解释,推荐帖子:

同步上下文(SynchronizationContext) 和 C#中跨线程更新UI的方法总结_c# synchronizationcontext_kalvin_y_liu的博客-CSDN博客

c#:深入理解SynchronizationContext_c# synchronizationcontext_jackletter的博客-CSDN博客

SynchronizationContext类的方法原型如下:

namespace System.Threading
{
    //
    // 摘要:
    //     提供在各种同步模型中传播同步上下文的基本功能。
    public class SynchronizationContext
    {
        //
        // 摘要:
        //     创建 System.Threading.SynchronizationContext 类的新实例。
        public SynchronizationContext();

        //
        // 摘要:
        //     获取当前线程的同步上下文。
        //
        // 返回结果:
        //     一个 System.Threading.SynchronizationContext 对象,它表示当前同步上下文。
        public static SynchronizationContext Current { get; }

        //
        // 摘要:
        //     设置当前同步上下文。
        //
        // 参数:
        //   syncContext:
        //     要设置的 System.Threading.SynchronizationContext 对象。
        [SecurityCritical]
        [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
        public static void SetSynchronizationContext(SynchronizationContext syncContext);
        //
        // 摘要:
        //     用于等待指定数组中的任一元素或所有元素接收信号的 Helper 函数。
        //
        // 参数:
        //   waitHandles:
        //     一个类型为 System.IntPtr 的数组,其中包含本机操作系统句柄。
        //
        //   waitAll:
        //     若等待所有句柄,则为 true;若等待任一句柄,则为 false。
        //
        //   millisecondsTimeout:
        //     等待的毫秒数,或为 System.Threading.Timeout.Infinite (-1),表示无限期等待。
        //
        // 返回结果:
        //     满足等待的对象的数组索引。
        [CLSCompliant(false)]
        [PrePrepareMethod]
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
        [SecurityCritical]
        protected static int WaitHelper(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout);
        //
        // 摘要:
        //     当在派生类中重写时,创建同步上下文的一个副本。
        //
        // 返回结果:
        //     一个新的 System.Threading.SynchronizationContext 对象。
        public virtual SynchronizationContext CreateCopy();
        //
        // 摘要:
        //     确定是否需要等待通知。
        //
        // 返回结果:
        //     如果需要等待通知,则为 true;否则为 false。
        public bool IsWaitNotificationRequired();
        //
        // 摘要:
        //     当在派生类中重写时,响应操作已完成的通知。
        public virtual void OperationCompleted();
        //
        // 摘要:
        //     当在派生类中重写时,响应操作已开始的通知。
        public virtual void OperationStarted();
        //
        // 摘要:
        //     当在派生类中重写时,将异步消息调度到一个同步上下文。
        //
        // 参数:
        //   d:
        //     要调用的 System.Threading.SendOrPostCallback 委托。
        //
        //   state:
        //     传递给委托的对象。
        public virtual void Post(SendOrPostCallback d, object state);
        //
        // 摘要:
        //     当在派生类中重写时,将一个同步消息调度到一个同步上下文。
        //
        // 参数:
        //   d:
        //     要调用的 System.Threading.SendOrPostCallback 委托。
        //
        //   state:
        //     传递给委托的对象。
        //
        // 异常:
        //   T:System.NotSupportedException:
        //     在 Windows Store 应用程序中调用的方法。用于 Windows Store 应用程序的 System.Threading.SynchronizationContext
        //     的实现应用不支持 System.Threading.SynchronizationContext.Send(System.Threading.SendOrPostCallback,System.Object)
        //     方法。
        public virtual void Send(SendOrPostCallback d, object state);
        //
        // 摘要:
        //     等待指定数组中的任一元素或所有元素接收信号。
        //
        // 参数:
        //   waitHandles:
        //     一个类型为 System.IntPtr 的数组,其中包含本机操作系统句柄。
        //
        //   waitAll:
        //     若等待所有句柄,则为 true;若等待任一句柄,则为 false。
        //
        //   millisecondsTimeout:
        //     等待的毫秒数,或为 System.Threading.Timeout.Infinite (-1),表示无限期等待。
        //
        // 返回结果:
        //     满足等待的对象的数组索引。
        //
        // 异常:
        //   T:System.ArgumentNullException:
        //     waitHandles 为 null。
        [CLSCompliant(false)]
        [PrePrepareMethod]
        [SecurityCritical]
        public virtual int Wait(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout);
        //
        // 摘要:
        //     设置指示需要等待通知的通知,并准备回调方法以使其在发生等待时可以更可靠地被调用。
        [SecuritySafeCritical]
        protected void SetWaitNotificationRequired();
    }
}

使用 SynchronizationContext 也可以实现切换线程执行的效果,只是平时这种方式在我们工作中用的并不是很多,具体案例就不做介绍了。

如果当前的文章对你有所帮助,欢迎点赞 + 留言,有疑问也可以私信,谢谢。

end

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/631466.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

点云综述(整理自网络资源)

目录 一、什么是点云 二、如何获取点云 1、三维激光扫描仪 2、双目相机 双目测距基本原理 视差图 双目测距的优点与难点 3、RGB-D相机 RGB-D什么意思 RGB-D相机的分类 RGBD相机的缺点&#xff1a; RGBD相机的优点 三、点云有哪些研究方向 1、基于点云的分类 2、基于…

华为OD机试真题 JavaScript 实现【IPv4地址转换成整数】【2023 B卷 100分】

一、题目描述 存在一种虚拟 IPv4 地址&#xff0c;由4小节组成&#xff0c;每节的范围为0~255&#xff0c;以#号间隔&#xff0c; 虚拟 IPv4 地址可以转换为一个32位的整数&#xff0c;例如&#xff1a; 128#0#255#255&#xff0c;转换为32位整数的结果为2147549183&#xff0…

【深入理解函数栈帧:探索函数调用的内部机制】

本章我们要介绍的不是数学中的函数&#xff0c;而是C语言中的函数哟&#xff01; 本章重点 了解汇编指令深刻理解函数调用过程 样例代码&#xff1a; #include <stdio.h> int MyAdd(int a, int b) {int c 0;c a b;return c; }int main() {int x 0xA;int y 0xB;int…

SpringCloud第二篇:Feign远程调用

思考&#xff1a;为啥要学Feign呢&#xff1f; 先来看我们以前利用RestTemplate发起远程调用的代码&#xff1a; String url "http://userservice/user/" order.getUserId(); User user restTemplate.getFor0bject(url,User.class);这里就有几个问题&#xff1a…

首届“设计·无尽谈”论坛完满收官 持续打造当代设计共同体

首届“设计无尽谈”论坛在京举行 5月16日&#xff0c;首届“设计无尽谈”论坛在北京举行&#xff0c;本次论坛以“漫谈当代空间精神”为主题&#xff0c;12位来自顶尖建筑设计领域的嘉宾和设计师到场&#xff0c;论坛以茶话会的形式进行&#xff0c;不受严格的议程和时间限制的…

计算机网络之网络层

四.网络层&#xff1a;数据平面 4.1 网络层概述 网络层被分解为两个相互作用的部分&#xff0c;即数据平面和控制平面。 数据平面决定到达路由器输入链路之一的数据报如何转发到该路由器的输出链路之一&#xff0c;转发方式有&#xff1a; 传统的IP转发&#xff1a;转发基于…

Nginx(一)介绍Nginx、正向代理和实现反向代理的两个实例

文章目录 一、Nginx介绍二、正向代理三、反向代理四、实例演示1、反向代理实例一&#xff08;反向代理&#xff0c;访问www.123.com&#xff09;2、反向代理实例二&#xff08;使用 nginx 反向代理&#xff0c;根据访问的路径跳转到不同端口的服务中&#xff09; 五、nginx之lo…

文件操作之文件下载(32)

下载和读取是差不多的情况 区分 文件被解析&#xff0c;我们称为文件包含漏洞 显示文件的源代码&#xff0c;我们称为文件读取漏洞 提示文件下载&#xff0c;我们称为文件下载漏洞 #文件下载 文件下载出现的原因&#xff0c;在任意代码里面出现下载性的功能性函数所导致的…

调用腾讯API实现人像分割

目录 1. 作者介绍2&#xff0e;腾讯云API人像分割2.1 人像分割接口描述2.2 请求参数介绍 3&#xff0e;代码实现3.1 获取SecretId和SecretKey3.2 人像分割代码调试3.3 完整代码3.4 实验结果 1. 作者介绍 岳泽昂&#xff0c;男&#xff0c;西安工程大学电子信息学院&#xff0c…

MySQL主从同步——主库已有的数据如何到从库

一、复制主库数据到从库 此步骤主要针对运行一段时间的主库&#xff0c;需要将历史数据导入到从库&#xff0c;保证主从强一致性。 主库锁表停止写操作 在主库MySQL命令行中执行 flush tables with read lock; 主库数据导出 将主库所在主机命令行下使用mysqldump命令导出…

交通状态分析 | Python实现基于张量分解的交通流量时空模式挖掘

文章目录 效果一览文章概述研究内容源码设计参考资料效果一览 文章概述 交通状态分析 | Python实现基于张量分解的交通流量时空模式挖掘 研究内容 一般出行行程通常都由某种明确目的驱使,例如上班、购物或娱乐,出行的起始区域因其承担功能的不同,通常能够反映出用户的出行目…

【一、Linux文件与目录结构】

1 Linux 文件 Linux系统中一切皆文件 2 Linux目录结构 /bin Binary的缩写&#xff0c;存放着命令。 /sbin s即Super User&#xff0c;存放着root用户使用的系统管理程序。 /home 存放着普通用户的主目录&#xff0c;在Linux中每个用户都有一个自己的目录&#xff0c;一般…

近期学习论文总结 3(23.06.05-23.06.09)

公众号&#xff1a;EDPJ 目录 0. 摘要 1. Arbitrary Style Transfer in Real-time with Adaptive Instance Normalization 1.1 主要思想 1.2 AdaIN 1.3 结构以及不同层使用 AdaIN 的效果 2. Watch your Up-Convolution: CNN Based Generative Deep Neural Networks are…

day46_项目

debug bug - 虫 第一台计算机,房子那么大,机械零件,齿轮,坏了,虫子(bug)卡着机器,debug(调试),虫子拿走了,机器就运行了,从此调试机器程序–>debug 目前: 这个卡机器的虫子,在博物馆 工具(IDEAEclipse)支持debug --> 追踪代码 如何使用debug 运行时候就得使用debug模式…

00后从事软件测试一年的心路历程

初识软件测试 不知不觉&#xff0c;我做软件测试已经快一年了&#xff0c;入职第一天的场景仿佛还在昨天。入职前&#xff0c;我对测试的认识仅仅停留在一些软件测试和测试方法的理论知识上&#xff0c;最多也是对自己的代码进行一些单元测试。 我之前所理解的测试是与开发分…

Django-可重用注册登录系统--项目搭建

文章目录 一、项目开始前的思考二、搭建项目环境三、设计数据库模型数据库模型文件设置数据库后端注册app生成迁移脚本并写入数据库测试是否成功数据库模型后台管理 路由与视图函数框架搭建路由配置视图函数的配置模板template的配置测试是否成功 前端界面设计与优化完善登录的…

【C/C++】函数参数默认值

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; &#x1f525;c系列专栏&#xff1a;C/C零基础到精通 &#x1f525; 给大…

【python+requests】接口自动化测试

这两天一直在找直接用python做接口自动化的方法&#xff0c;在网上也搜了一些博客参考&#xff0c;今天自己动手试了一下。 一、整体结构 上图是项目的目录结构&#xff0c;下面主要介绍下每个目录的作用。 Common:公共方法:主要放置公共的操作的类&#xff0c;比如数据库sql…

VPN(Virtual privacte network)浅谈

文章目录 VPN概念VPN类型站点-站点VPN客户端-站点VPN VPN的工作原理VPN职责职责一&#xff1a;保密完整性认证PSK算法实现&#xff08;献给大佬&#xff09;PSK应用演示RSA算法实现&#xff08;献给大佬&#xff09;RSA应用演示&#xff1a;实现签名 VPN两大框架VPN的误解VPN合…

Java调用Pytorch实现以图搜图(附源码)

Java调用Pytorch实现以图搜图 设计技术栈&#xff1a; 1、ElasticSearch环境&#xff1b; 2、Python运行环境&#xff08;如果事先没有pytorch模型时&#xff0c;可以用python脚本创建模型&#xff09;&#xff1b; 1、运行效果 2、创建模型&#xff08;有则可以跳过&#xf…