天天看点

C#中的多线程,学习笔记多线程

多线程

操作系统的进程和线程,线程就是围绕进程的一个子单元。

理解多线程

例 1

界面上点击某个按钮后,需要执行一个非常耗时的操作,如果不使用多线程,就只能傻等操作返回。

使用多线程,点击按钮之后,开辟1个新线程后台去执行这个耗时操作,前台界面继续执行其他菜单目录,录入数据等。

例 2

有个操作,1个线程需要20分钟完成,现在的多核cpu,可以真正同一时刻运行多个线程。

假设是双核cpu,同一时刻运行2个线程,操作就只需要10分钟可以完成。

多线程优点:

1提高应用程序执行效率【例1】

2提高CPU利用率【例2】

线程的数量

理想的线程数量<=CPU核心数量
           

Thread类

Thread thread = new Thread(SayHi);
thread.Start();
           

线程的挂起

Thread.Sleep(1000);
           

线程的阻塞

示例:

for (var i = 0; i < 5; i++)
            {
                Thread th1 = new Thread(DoSomething);
                th1.Start();
                th1.Join();
            }
           

阻塞后,各线程会依次执行。

线程终止,不推荐使用,不安全不可控。

前台线程、后台线程

Thread thread = new Thread(() =>
{
    // 默认为 False
    Console.WriteLine(Thread.CurrentThread.IsBackground);
});
// 可以设置为后台线程
//thread.IsBackground = true;
thread.Start();
thread.Join();
           

.Net的公用语言运行时(Common Language Runtime,CLR)能区分两种不同类型的线程:前台线程和后台线程。这两者的区别就是:应用程序必须运行完所有的前台线程才可以退出;而对于后台线程,应用程序则可以不考虑其是否已经运行完毕而直接退出,所有的后台线程在应用程序退出时都会自动结束。

一般后台线程用于处理时间较短的任务,如在一个Web服务器中可以利用后台线程来处理客户端发过来的请求信息。而前台线程一般用于处理需要长时间等待的任务,如在Web服务器中的监听客户端请求的程序,或是定时对某些系统资源进行扫描的程序。

重要性

(1)有前台,不关闭

如果线程为前台线程,可能导致UI线程已关闭,但实际还有前台线程暗地里运行,所以程序并没有真正关闭。

(2)无前台,全关闭

当然也要注意,如果所有前台线程都关闭,后台线程会自动关闭,后台线程的代码逻辑可能没执行完就终止了。

线程传递参数

1使用ParameterizedThreadStart委托

注意:此处只能接受object类型参数,因为Thread中只接受ThreadStart委托,而该委托是一个无参无返回值的。所以此处使用ParameterizedThreadStart。

ParameterizedThreadStart pts1 = DoSomething;
     Thread thread = new Thread(pts1);
     object name = "线程参数";
     thread.Start(name);


           

在线程执行过程中,该参数需要使用需要这是一个只读属性的对象

static readonly object obj = new object();
   public static void DoSomething(string name)
        {
            Console.WriteLine("线程{0}启动", Thread.CurrentThread.ManagedThreadId);
            Stopwatch sw = new Stopwatch();
            sw.Start();
            long result = 0;
            for (int i = 0; i < 1000000000; i++)
            {
                result += i;
            }
            Thread.Sleep(2000);
            sw.Stop();
            Console.WriteLine("任务名称={3},当前线程={0},计算结果是{1},总耗时:{2}", Thread.CurrentThread.ManagedThreadId, result, sw.ElapsedMilliseconds, obj.ToString());
        }

  
           

2使用lambda传递参数(推荐)

string hi = "张三";
 Thread thread = new Thread(() =>
 {
     DoSomething(hi);
 });
 thread.Start();
           

线程异常

注意:只要有线程抛出异常,整个程序就会停止。所以,异常必须处理。

对创建线程try catch没用,需要对线程中调用的函数添加异常处理代码

static void Main(string[] args)
{
    try // 无用的 try catch
    {
        Thread thread = new Thread(SayHi);
        thread.Start();
    }
    catch (Exception ex)
    {
        // 不会打印错误
        Console.WriteLine(ex.ToString());
    }
}
//正确
static void Main(string[] args)
{
    Thread thread = new Thread(SayHi);
    thread.Start();
    thread.Join();
}
static void SayHi()
{
    try
    {
        Console.WriteLine("hi");
        throw new Exception("error");
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
}
           

线程安全

试试下列代码:

static void Main(string[] args)
{
    Thread thread = new Thread(
        () => { Check("张三"); });
    Thread thread2 = new Thread(
        () => { Check("李四"); });
    thread.Start();
    thread2.Start();
    thread.Join();
    thread2.Join();
}
static void Check(string name)
{
    User.Name = name;
    string format = @"{0} = {1}";
    for (int i = 0; i < 10; i++)
    {
        string s = string.Format(format, name, User.Name);
        Console.WriteLine(s);
        Thread.Sleep(10);
    }
}
public class User
{
    public static string Name { get; set; }
}
           

张三等于了李四

原因:User.Name是静态属性,也就是共享资源,多个线程访问共享资源,需要对共享资源做同步处理。

如何解决:

创建一个只读对象对对象处理使用lock(锁操作)

基本含义: lock就是把一段代码定义为临界区,所谓临界区就是同一时刻只能有一个线程来操作临界区的代码,当一个线程位于代码的临界区时,另一个线程不能进入临界区,如果 试图进入临界区,则只能一直等待(即被阻止),直到已经进入临界区的线程访问完毕,并释放锁旗标

static readonly object obj = new object();
// -------------------------
lock (obj)
{
    User.Name = name;
    string format = @"{0} = {1}";
    for (int i = 0; i < 10; i++)
    {
        string s = string.Format(format, name, User.Name);
        Console.WriteLine(s);
        Thread.Sleep(10);
    }
}
           

多线程的缺点

1线程占用一定资源,比如内存,CPU;创建和销毁操作都比较昂贵

2线程调度器要管理线程

3大量的创建线程,导致内存不够用,线程调度器繁忙。

4当多个线程竞争同一个静态资源时,会造成死锁。