c#多线程(一)——基础概念和基本使用

一、多线程相关的基本概念

进程(Process):是系统中的一个基本概念。 一个正在运行的应用程序在操作系统中被视为一个进程,包含着一个运行程序所需要的资源,进程可以包括一个或多个线程 。进程之间是相对独立的,一个进程无法访问另一个进程的数据(除非利用分布式计算方式),一个进程运行的失败也不会影响其他进程的运行,Windows系统就是利用进程把工作划分为多个独立的区域的。进程可以理解为一个程序的基本边界。

线程(Thread):是 进程中的基本执行单元,是操作系统分配CPU时间的基本单位 ,在进程入口执行的第一个线程被视为这个进程的 主线程 。在.NET应用程序中,都是以Main()方法作为入口的,当调用此方法时系统就会自动创建一个主线程。

多线程能实现的基础:

  1、CPU运行速度太快,硬件处理速度跟不上,所以操作系统进行分时间片管理。这样,宏观角度来说是多线程并发 ,看起来是同一时刻执行了不同的操作。但是从微观角度来讲,同一时刻只能有一个线程在处理。

  2、目前电脑都是多核多CPU的,一个CPU在同一时刻只能运行一个线程,但是 多个CPU在同一时刻就可以运行多个线程 。

多线程的优点:

  可以同时完成多个任务;可以让占用大量处理时间的任务或当前没有进行处理的任务定期将处理时间让给别的任务;可以随时停止任务;可以设置每个任务的优先级以优化程序性能。

多线程的缺点:

  1、 内存占用  线程也是程序,所以线程需要占用内存,线程越多,占用内存也越多。

  2、 管理协调 多线程需要协调和管理,所以需要占用CPU时间以便跟踪线程,线程太多会导致控制太复杂。

  3、 资源共享   线程之间对共享资源的访问会相互影响,必须解决争用共享资源的问题。

二、C#中的线程使用

1、基本使用

1、无参

  ①调用静态方法

      static void Main(string[] args)
        {
            //无参调用静态方法
            Thread thread1 = new Thread(Func1);
            thread1.Start(); 
        }
        static void Func1()
        {
            Console.WriteLine("这是静态方法");
        }

  ②调用实例方法

 1      class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             ThreadTest test = new ThreadTest();
 6             //无参调用实例方法
 7             Thread thread1 = new Thread(test.Func2);
 8             thread1.Start();
 9             Console.ReadKey();
10         }
11     }
12 
13     class ThreadTest
14     {
15         public void Func2()
16         {
17             Console.WriteLine("这是实例方法");
18         }
19     }

2、有参数时

 1      class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             ThreadTest test = new ThreadTest();
 6             //有参调用实例方法,ParameterizedThreadStart是一个委托,input为object,返回值为void
 7             Thread thread1 = new Thread(new ParameterizedThreadStart(test.Func1));
 8             thread1.Start("有参的实例方法");
 9             Console.ReadKey();
10         }
11     }
12     class ThreadTest
13     {
14         public void Func1(object o)
15         {
16             Console.WriteLine(o);
17         }
18     }

2、常用的属性和方法

属性名称 说明
CurrentThread 获取当前正在运行的线程。
ExecutionContext 获取一个 ExecutionContext 对象,该对象包含有关当前线程的各种上下文的信息。
IsBackground bool,指示某个线程是否为后台线程。
IsThreadPoolThread bool,指示线程是否属于托管线程池。
ManagedThreadId int,获取当前托管线程的唯一标识符。
Name string,获取或设置线程的名称。
Priority

获取或设置一个值,该值指示线程的调度优先级 。

Lowest<BelowNormal<Normal<AboveNormal<Highest

ThreadState

获取一个值,该值包含当前线程的状态。

Unstarted、Sleeping、Running 

方法名称 说明
Abort()     终止本线程。
GetDomain() 返回当前线程正在其中运行的当前域。
GetDomainId() 返回当前线程正在其中运行的当前域Id。
Suspend() 挂起当前线程,如果当前线程已属于挂起状态则此不起作用
Interrupt() 中断处于 Wait/Sleep/Join 线程状态的线程。
Resume() 继续运行已挂起的线程。
Start()   执行本线程。(不一定立即执行,只是标记为可以执行)
Join() 阻塞调用线程,直到某个线程终止时为止。
Sleep()   把正在运行的线程挂起一段时间。

3、线程同步  

  所谓同步: 是指在某一时刻只有一个线程可以访问变量 。
  c#为同步访问变量提供了一个非常简单的方式,即使用c#语言的关键字Lock,它可以把一段代码定义为互斥段,互斥段在一个时刻内只允许一个线程进入执行,实际上是Monitor.Enter(obj),Monitor.Exit(obj)的语法糖。在c#中,lock的用法如下:

 lock (obj) { dosomething... }

obj代表你希望锁定的对象,注意一下几点:

  1. lock不能锁定空值 ,因为Null是不需要被释放的。 2. 不能锁定string类型 ,虽然它也是引用类型的。因为字符串类型被CLR“暂留”,这意味着整个程序中任何给定字符串都只有一个实例,具有相同内容的字符串上放置了锁,就将锁定应用程序中该字符串的所有实例。 3. 值类型不能被lock ,每次装箱后的对象都不一样 ,锁定时会报错 4  避免锁定public类型 如果该实例可以被公开访问,则 lock(this) 可能会有问题,因为不受控制的代码也可能会锁定该对象。这可能导致死锁,即两个或更多个线程等待释放同一对象。 

         推荐使用 private static readonly类型的对象,readonly是为了避免lock的代码块中修改对象,造成对象改变后锁失效。

以书店卖书为例

 1      class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             BookShop book = new BookShop();
 6             //创建两个线程同时访问Sale方法
 7             Thread t1 = new Thread(book.Sale);
 8             Thread t2 = new Thread(book.Sale);
 9             //启动线程
10             t1.Start();
11             t2.Start();
12             Console.ReadKey();
13         }
14     }
15     class BookShop
16     {
17         //剩余图书数量
18         public int num = 1;
19         private static readonly object locker = new object();
20         public void Sale()
21         {
22 
23             lock (locker)
24             {
25                 int tmp = num;
26                 if (tmp > 0)//判断是否有书,如果有就可以卖
27                 {
28                     Thread.Sleep(1000);
29                     num -= 1;
30                     Console.WriteLine("售出一本图书,还剩余{0}本", num);
31                 }
32                 else
33                 {
34                     Console.WriteLine("没有了");
35                 }
36             }
37         }
38     }

 代码执行结果时:

如果不添加lock则执行的结果时:

4、跨线程访问

例子:点击测试按钮,给文本框赋值

代码如下:

 1     private void myBtn_Click(object sender, EventArgs e)
 2         {
 3             Thread thread1 = new Thread(SetValue);
 4             thread1.Start();
 5 
 6         }
 7         private void SetValue()
 8         {
 9             for (int i = 0; i < 10000; i++)
10             {
11                 this.myTxtBox.Text = i.ToString();
12             }
13         }

 执行代码会出现如下错误:

出现该错误的原因是:myTxtBox是由主线程创建的,thread1线程是另外一个线程,在.NET上执行的是托管代码, C#强制要求这些代码必须是线程安全的,即不允许跨线程访问Windows窗体的控件 

 解决的方法:

 1      private delegate void setTextValueCallBack(int value);
 2         //声明回调
 3         private setTextValueCallBack setCallBack;
 4         private void myBtn_Click(object sender, EventArgs e)
 5         {
 6             //实例化回调
 7             setCallBack = new setTextValueCallBack(SetValue);
 8             //创建一个线程去执行这个方法:创建的线程默认是前台线程
 9             Thread thread = new Thread(Test);
10             //将线程设置为后台线程
11             thread.IsBackground = true;
12             thread.Start();
13         }
14 
15         private void Test()
16         {
17             for (int i = 0; i < 10000; i++)
18             {
19                 //使用回调
20                 this.myTxtBox.Invoke(setCallBack, i);
21             }
22         }
23 
24         /// <summary>
25         /// 定义回调使用的方法
26         /// </summary>
27         /// <param name="value"></param>
28         private void SetValue(int value)
29         {
30             this.myTxtBox.Text = value.ToString();
31         }

  invoke:在“拥有控件的基础窗口句柄的线程”  即在本例的主线程上执行委托,这样就不存在跨线程访问了 ,因此还是线程安全的。

参考文章:

[1] https://www.cnblogs.com/dotnet261010/p/6159984.html

[2] https://www.cnblogs.com/wwj1992/p/5976096.html

猜你喜欢

转载自www.cnblogs.com/wyy1234/p/9166444.html