C# 学习笔记(9)线程

本文参考博客
C#多线程 https://www.cnblogs.com/dotnet261010/p/6159984.html
C# 线程与进程 https://www.cnblogs.com/craft0625/p/7496682.html
C# 线程同步https://www.cnblogs.com/dotnet261010/p/12335538.html
对于c#中的线程和进程,这几篇文章讲的相当到位了,本文只是为了学习做的摘要。

线程间数据同步

看下面一个例子,有线程t1和t2 都操作变量 Counter 自增一千次,理论上我们最后应该得到 Counter = 2000;

    public partial class Form1 : Form{public Form1(){InitializeComponent();}private static int Counter = 0;private void btnPrint_Click(object sender, EventArgs e){Counter = 0;Thread t1 = new Thread(() => {for (int i = 0; i < 1000; i++){Counter++;Thread.Sleep(1);}});t1.Start();Thread t2 = new Thread(() => {for (int i = 0; i < 1000; i++){Counter++;Thread.Sleep(1);}});t2.Start();//等待线程结束while (!(t1.ThreadState == ThreadState.Stopped && t2.ThreadState == ThreadState.Stopped)) ;txbLog.AppendText(Counter.ToString() + "\r\n");}}

但是实际上你会发现,线程t1和线程t2结束后 Counter的值并不会是预期的2000,而且还会有波动,为什么?

假设t1线程读取到Counter的值为200,可能t2线程执行非常快,t1线程读取Counter值的时候,t2线程已经把Counter的值改为了205,等t1线程执行完毕以后,Counter的值又被变为了201,这样就会出现线程同步的问题了。那么该如何解决这个问题呢?

lock

lock是C#中的关键字,可以锁定一个资源,lock的特点是:同一时刻只能有一个线程进入lock的对象的范围,其它lock的线程都要等待。如果研究嵌入式,其实就是嵌入式RTOS中的互斥信号量

    public partial class Form1 : Form{public Form1(){InitializeComponent();}private static int Counter = 0;static object loker = new object();private void btnPrint_Click(object sender, EventArgs e){Counter = 0;Thread t1 = new Thread(() => {for (int i = 0; i < 1000; i++){lock(loker){Counter++;}Thread.Sleep(1);}});t1.Start();Thread t2 = new Thread(() => {for (int i = 0; i < 1000; i++){lock (loker){Counter++;}Thread.Sleep(1);}});t2.Start();//等待线程结束while (!(t1.ThreadState == ThreadState.Stopped && t2.ThreadState == ThreadState.Stopped)) ;txbLog.AppendText(Counter.ToString() + "\r\n");}}

添加lock后 结果符合预期

同步方法

除了使用关键字 lock外 还可以将方法标记为同步方法

public partial class Form1 : Form{public Form1(){InitializeComponent();}private static int Counter = 0;static object loker = new object();//将方法标记为同步方法[MethodImpl(MethodImplOptions.Synchronized)]static void Test(){Counter++;}private void btnPrint_Click(object sender, EventArgs e){Counter = 0;Thread t1 = new Thread(() => {for (int i = 0; i < 1000; i++){Test();Thread.Sleep(1);}});t1.Start();Thread t2 = new Thread(() => {for (int i = 0; i < 1000; i++){Test();Thread.Sleep(1);}});t2.Start();//等待线程结束while (!(t1.ThreadState == ThreadState.Stopped && t2.ThreadState == ThreadState.Stopped)) ;txbLog.AppendText(Counter.ToString() + "\r\n");}}

利用委托自动创建线程

    public partial class Form1 : Form{public Form1(){InitializeComponent();}//声明委托类delegate void txbLogPrintDelegate();void PrintfLog(){for (int i = 0; i < 10000; i++){//通过txbLog的InVoke 告诉创建txbLog的线程,需要操作txbLog控件txbLog.Invoke((Action<string>)TxbLogAppendText, "这是第" + i + "行\r\n");Thread.Sleep(1);}}void TxbLogAppendText(string str){//创建txbLog的线程也就是主线程 操作txbLog控件this.txbLog.AppendText(str);}private void btnPrint_Click(object sender, EventArgs e){创建线程//Thread printThread = new Thread(PrintfLog);告诉系统,这个线程准备好了,可以开始执行了,至于什么时候执行,看系统安排//printThread.Start();//通过委托创建线程txbLogPrintDelegate testDelegate = PrintfLog;testDelegate.BeginInvoke(null, null);}}

同步与异步

利用委托自动创建线程时 有 BeginInvoke 和 Invoke两种,区别就是 Invoke是同步的,当工作线程运行结束才会执行下一行代码,BeginInvoke 是异步的。

带参数线程

使用thread 创建的线程,参数只能是 object类型的,object是所有类型的基类,因此可以装其他的所有类型,不过需要自己进行类型转换。

    public partial class Form1 : Form{public Form1(){InitializeComponent();}//声明委托类delegate void txbLogPrintDelegate(int num);void PrintfLog(object num){for (int i = 0; i < (int)num; i++){//通过txbLog的InVoke 告诉创建txbLog的线程,需要操作txbLog控件txbLog.Invoke((Action<string>)TxbLogAppendText, "这是第" + i + "行\r\n");Thread.Sleep(1);}}void TxbLogAppendText(string str){//创建txbLog的线程也就是主线程 操作txbLog控件this.txbLog.AppendText(str);}private void btnPrint_Click(object sender, EventArgs e){//创建线程Thread printThread = new Thread(PrintfLog);//告诉系统,这个线程准备好了,可以开始执行了,至于什么时候执行,看系统安排printThread.Start(1000);通过委托创建线程//txbLogPrintDelegate testDelegate = PrintfLog;//testDelegate.BeginInvoke(100, null, null);}}

回调

当委托创建的异步线程执行结束时,想要在最后添加一个操作,可以利用异步委托的回调来实现(当然也可以将该操作添加到异步线程结束前,但是这样有可能会破坏模块封装性)。

    public partial class Form1 : Form{public Form1(){InitializeComponent();}//声明委托类delegate void txbLogPrintDelegate(int num);void PrintfLog(int num){for (int i = 0; i < (int)num; i++){//通过txbLog的InVoke 告诉创建txbLog的线程,需要操作txbLog控件txbLog.Invoke((Action<string>)TxbLogAppendText, "这是第" + i + "行\r\n");Thread.Sleep(1);}}void TxbLogAppendText(string str){//创建txbLog的线程也就是主线程 操作txbLog控件this.txbLog.AppendText(str);}private void btnPrint_Click(object sender, EventArgs e){//通过委托创建线程txbLogPrintDelegate testDelegate = PrintfLog;//创建回调函数 异步线程结束时会调用该回调函数AsyncCallback asyncCallback = p => { txbLog.Invoke((Action<string>)TxbLogAppendText, "回调函数  结束\r\n"); };testDelegate.BeginInvoke(100, asyncCallback, null);}}

C# 学习笔记(9)线程相关推荐

  1. java学习笔记15--多线程编程基础2

    本文地址:http://www.cnblogs.com/archimedes/p/java-study-note15.html,转载请注明源地址. 线程的生命周期 1.线程的生命周期 线程从产生到消亡 ...

  2. java学习笔记14--多线程编程基础1

    本文地址:http://www.cnblogs.com/archimedes/p/java-study-note14.html,转载请注明源地址. 多线程编程基础 多进程 一个独立程序的每一次运行称为 ...

  3. Python学习笔记:线程和进程(合),分布式进程

    前言 最近在学习深度学习,已经跑出了几个模型,但Pyhton的基础不够扎实,因此,开始补习Python了,大家都推荐廖雪峰的课程,因此,开始了学习,但光学有没有用,还要和大家讨论一下,因此,写下这些帖 ...

  4. 0040 Java学习笔记-多线程-线程run()方法中的异常

    run()与异常 不管是Threade还是Runnable的run()方法都没有定义抛出异常,也就是说一条线程内部发生的checked异常,必须也只能在内部用try-catch处理掉,不能往外抛,因为 ...

  5. 【Java学习笔记】线程学习笔记

    一.资源 http://blog.csdn.net/axman/article/details/431796 这个博客里有Java多线程.线程池的一系列,从基础开始就很清楚,牛人 二.重点 今天读到了 ...

  6. 多线程学习笔记——判断线程状态

    一. C#多线程状态判断http://blog.csdn.net/deandingding/article/details/39762489 C#多线程有很多值得学习的地方,这里我们主要介绍C# Th ...

  7. 初探swift语言的学习笔记五(线程)

    作者:fengsh998 原文地址:http://blog.csdn.net/fengsh998/article/details/30354127 转载请注明出处 假设认为文章对你有所帮助,请通过留言 ...

  8. C#学习笔记之线程 - 同步上下文

    同步上下文(Synchronization Contexts) 手动使用锁的一个替代方案是去声明锁.通过派生ContextBoundObject和应用Synchronization属性,你告诉CLR自 ...

  9. 《Windows via C/C++》学习笔记 —— Windows 线程池

    线程池(thread pool),允许有多个线程同时存在,并发执行,并且这些线程受到统一管理. 在Windows Vista中,提供了全新的线程池机制,一般这些线程池中的线程的创建的销毁是由操作系统自 ...

  10. iOS学习笔记11-多线程入门

    一.iOS多线程 iOS多线程开发有三种方式: NSThread NSOperation GCD iOS在每个进程启动后都会创建一个主线程,更新UI要在主线程上,所以也称为UI线程,是其他线程的父线程 ...

最新文章

  1. python将第一列替换_python-通过将另一列与第二个DataFrame进行比较来替换一列中的值...
  2. awk中如何使用shell的环境变量
  3. sql语句优化(二)
  4. .NET常用工具类集锦
  5. LeetCode 536. 从字符串生成二叉树(递归)
  6. Python添加模块路径
  7. Android笔记 解析xml文件demo
  8. 晨哥真有料丨这样的你很掉价!
  9. Opencv step by step - 图像变换
  10. 关于selenium获取cookie然后实现免登录
  11. Centos 7环境MySql8.0.28源码安装
  12. 最强代码审查工具报告
  13. memcached入门
  14. 跑revit计算机硬件要求,什么样的电脑能流畅跑Revit?Revit对电脑配置要求
  15. python和c 情侣网名_带符号的qq情侣网名 好听的情侣网名大全
  16. 什么是指令重排序?为什么要重排序?
  17. 基于Python的百度AI人脸识别API接口(可用于OpenCV-Python人脸识别)
  18. 错误:找不到符号 类XXX 位置:程序包 com.xxx.xxx
  19. 传奇假人自动上线_传奇商业脚本 各种M2防假人脚本大集合 传奇私服脚本
  20. 获取android设备唯一编号_android获取设备唯一标识完美解决方案的思考以及实现方式...

热门文章

  1. nginx 网站目录重写
  2. hdu4585 amp; BestCoder Round #1 项目管理(vector应用)
  3. *.tar.bz2文件解压
  4. ROS集成开发环境搭建
  5. 阶段-关系系统-stage1范围界定阶段---学习记录
  6. 广佛肇城轨年内通车 佛山西站预计2017年中通车
  7. 技术团队新官上任之基层篇
  8. WINCE下实现USB转RS232
  9. Windows Azure Platform Introduction (9) 申请Windows Azure 账户
  10. elastic search2.3.1(3) 查询语句拼接实战termQuery ,matchQuery, boolQuery, rangeQuery, wildcardQuery...