C#多线程基础

本文参考他人博客:
C# 多线程编程——理解多线程(一)
https://blog.csdn.net/kingshown_WZ/article/details/89074039
C# 多线程编程实战(一)–线程基础
https://blog.csdn.net/qq_21419015/article/details/80089032

一.C#多线程基础简介

1.1什么是多线程

进程:当一个程序开始运行时,它就是一个进程,进程包括运行中的程序和程序所使用到的内存和系统资源。而一个进程又是由多个线程所组成的。
线程:线程是程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针、程序计数器等),但代码区是共享的,即不同的线程可以执行同样的函数。
多线程:多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。
静态属性:这个类所有对象所公有的属性,不管你创建了多少个这个类的实例,但是类的静态属性在内存中只有一个。

1.2多线程优劣

优点:可以提高CPU的利用率。在多线程程序中,一个线程必须等待的时候,CPU可以运行其它的线程而不是等待,这样就大大提高了程序的效率。
缺点:线程也是程序,所以线程需要占用内存,线程越多占用内存也越多;
多线程需要协调和管理,所以需要CPU时间跟踪线程;
线程之间对共享资源的访问会相互影响,必须解决竞用共享资源的问题;
线程太多会导致控制太复杂,最终可能造成很多Bug;

二.控制线程的类和方法

2.1 System.Threading.Thread类

System.Threading.Thread是用于控制线程的基础类,通过Thread可以控制当前应用程序域中线程的创建、挂起、停止、销毁。

2.1.1Thread类公共属性

在这里插入图片描述

2.1.2线程优先级别

在这里插入图片描述

2.1.3 线程常用方法

Thread 中包括了多个方法来控制线程的创建、挂起、停止、销毁。
在这里插入图片描述

三.常用基础代码

3.1创建线程

引用命名空间:

using System.Threading;

创建线程对象关键字 Thread
创建一个线程一般包含三个步骤:
1、创建入口函数
2、创建入口委托
3、创建线程

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

namespace MultithreadingTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Thread t1 = new Thread(new ThreadStart(PrintNumbers));//无参数的委托
            t1.Start();

            Thread t2 = new Thread(new ParameterizedThreadStart(PrintNumbers));//有参数的委托
            t2.Start(10);
            Console.ReadKey();

        }
        static void PrintNumbers()
        {
            Console.WriteLine("PrintNumbers Starting...");
            for (int i = 0; i < 10; i++)
            {
                Console.Write(i);
            }
            Console.WriteLine();
        }

        //注意:要使用ParameterizedThreadStart,定义的参数必须为object
        static void PrintNumbers(object count)
        {
            Console.WriteLine("PrintNumbers Starting...");
            for (int i = 0; i < Convert.ToInt32(count); i++)
            {
                Console.Write(i);
            }
            Console.WriteLine();
        }


    }
}

通过Thread实例化出不同的线程实例t1,t2实现两个进程分别打印数据;结果如下
在这里插入图片描述

3.2线程阻塞

一个线程进入阻塞状态可能的原因:
①通过调用sleep(millseconds)使任务进入休眠状态;时间一到自动恢复执行,其特点是不占用CPU,其他线程可以继续执行。
②线程执行wait方法时,需要其他线程调用Monitor.Pulse()或者Monitor.PulseAll()进行唤醒或者说是通知等待的队列。
③任务在等待某个输入 / 输出流的完成;
④任务试图在某个对象上调用其同步控制方法,但是对象锁不可用,因为另一个任务已经获取了该锁;

sleep() 实例

sleep(millseconds)//间隔多少秒执行下一语句
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace MultithreadingTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Thread t1 = new Thread(PrintNumbersWithDelay);
            t1.Start();
            PrintNumbers();
            Console.ReadLine();
            Console.ReadKey();

        }
        static void PrintNumbers()
        {
            Console.WriteLine("PrintNumbers Starting...");
            for (int i = 0; i < 10; i++)
            {

                Console.Write(i);
            }
            Console.WriteLine();
        }

        static void PrintNumbersWithDelay()
        {
            Console.WriteLine("PrintNumbersWithDelay Starting...");
            for (int i = 0; i < 10; i++)
            {
                Thread.Sleep(TimeSpan.FromSeconds(2));
                Console.Write(i);
            }
            Console.WriteLine();
        }




    }
}

结果如下;第二行每两秒输出一个数,直到输出所有数:
在这里插入图片描述

3.3线程等待

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

namespace MultithreadingTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Starting...");
            Thread th = new Thread(PrintNumbersWithDelay);
            th.Start();
            th.Join();   //使用Join等待th完成
            PrintNumbers();
            Console.WriteLine("THread Complete");
            Console.ReadLine();


        }
        static void PrintNumbers()
        {
            Console.WriteLine("PrintNumbers Starting...");
            for (int i = 0; i < 10; i++)
            {
                Console.WriteLine(i);
            }
        }

        static void PrintNumbersWithDelay()
        {
            Console.WriteLine("PrintNumbersWithDelay Starting...");
            for (int i = 0; i < 10; i++)
            {
                Thread.Sleep(TimeSpan.FromSeconds(2));
                Console.WriteLine(i);
            }
        }


    }
}

只有th执行完成后才执行其他程序;结果如下
在这里插入图片描述
如果没有

th.Join();   //使用Join等待th完成

结果如下
在这里插入图片描述
所以有想要让线程和函数按照顺序执行,必须要在start()后加上Join()

3.4终止线程

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

namespace MultithreadingTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Starting Program...");
            Thread t1 = new Thread(PrintNumbersWithDelay);
            t1.Start();
            Thread.Sleep(TimeSpan.FromSeconds(6));
          t1.Abort();    //使用Abort()终止线程
            Console.WriteLine("Thread t1 has been aborted");
            Thread t2 = new Thread(PrintNumbers);
            PrintNumbers();
            Console.ReadLine();
        }

        static void PrintNumbers()
        {
            Console.WriteLine("PrintNumbers Starting...");
            for (int i = 0; i < 10; i++)
            {

                Console.WriteLine(i);
            }
        }

        static void PrintNumbersWithDelay()
        {
            Console.WriteLine("PrintNumbers Starting...");
            for (int i = 0; i < 10; i++)
            {
                Thread.Sleep(TimeSpan.FromSeconds(2));
                Console.WriteLine(i);
            }
        }
    }
}

效果:
在这里插入图片描述
如果没有 t1.Abort(); //使用Abort()终止线程
在这里插入图片描述

3.6 检测线程状态

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

namespace MultithreadingTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Start Program...");
            Thread t1 = new Thread(Status);
            Thread t2 = new Thread(OnlySleep);
            Console.WriteLine(t1.ThreadState.ToString());//获取实例线程状态
            t2.Start();
            t1.Start();
            for (int i = 0; i < 20; i++)
            {
                Console.WriteLine(t1.ThreadState.ToString());
            }
            Thread.Sleep(TimeSpan.FromSeconds(6));
            t1.Abort();
            Console.WriteLine("thread t1 has been aborted");
            Console.WriteLine(t1.ThreadState.ToString());
            Console.WriteLine(t2.ThreadState.ToString());
            Console.ReadLine();
        }

        private static void Status()
        {
            Console.WriteLine("Starting...");
            Console.WriteLine(Thread.CurrentThread.ThreadState.ToString());//获取当前线程状态
            for (int i = 0; i < 10; i++)
            {
                Thread.Sleep(TimeSpan.FromSeconds(2));
                Console.WriteLine(i);
            }
        }

        private static void OnlySleep()
        {
            Thread.Sleep(TimeSpan.FromSeconds(2));
        }

    }
}

结果如下:
在这里插入图片描述
注释:使用Thread.ThreadState获取线程的运行状态。ThreadState是一个C#枚举。谨记:不要在程序中使用线程终止,否则可能会出现意想不到的结果

3.6线程优先级

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

using System.Diagnostics;


namespace MultithreadingTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Current thread priority: {0}", Thread.CurrentThread.Priority);
            Console.WriteLine("Running on all cores available");//获取实例线程状态
            RunThreads();

            Thread.Sleep(TimeSpan.FromSeconds(2));
            Console.WriteLine("Running on a single Core");
            //让操作系统的所有线程运行在单个CPU核心上
            Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(1);
            RunThreads();
            Console.ReadLine();
        }

        private static void RunThreads()
        {
            var sample = new ThreadSample();

            var t1 = new Thread(sample.CountNumbers);
            t1.Name = "Thread One";
            var t2 = new Thread(sample.CountNumbers);
            t2.Name = "Thread Two";

            t1.Priority = ThreadPriority.Highest;//使用Priority设置线程的优先级
            t2.Priority = ThreadPriority.Lowest;
            t1.Start();
            t2.Start();

            Thread.Sleep(TimeSpan.FromSeconds(2));
            sample.Stop();
        }
    }

    class ThreadSample
    {
        private bool _isStopped = false;
        public void Stop()
        {
            _isStopped = true;
        }

        public void CountNumbers()
        {
            long counter = 0;
            while (!_isStopped)
            {
                counter++;
            }
            Console.WriteLine("{0} with {1} priority has a count={2}", Thread.CurrentThread.Name, Thread.CurrentThread.Priority, counter.ToString("NO"));
        }


    }
}

在这里插入图片描述
注释:单核执行多线程耗费的时间比多核的多很多

3.7前台线程和后台线程

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

using System.Diagnostics;


namespace MultithreadingTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var sampleForground = new ThreadSample(10);
            var sampleBackground = new ThreadSample(20);
            var t1 = new Thread(sampleForground.CountNumbers);
            t1.Name = "ForegroundThread";   //没有明确声明的均为前台线程
            var t2 = new Thread(sampleBackground.CountNumbers);
            t2.Name = "BackgroundThread";
            t2.IsBackground = true;    //设置为后台线程

            t1.Start();
            t2.Start();
            Console.ReadKey();
        }
    }

    class ThreadSample
    {
        private readonly int _iteration;

        public ThreadSample(int iteration)
        {
            _iteration = iteration;
        }

        public void CountNumbers()
        {
            for (int i = 0; i < _iteration; i++)
            {
                Thread.Sleep(TimeSpan.FromSeconds(0.5));
                Console.WriteLine("{0} prints {1}", Thread.CurrentThread.Name, i);
            }
        }



    }
}

结果如下:
在这里插入图片描述

3.8向线程传递参数

using System;
using System.Diagnostics;
using System.Threading;
 
namespace MulityThreadNote
{
    class Program
    {
        static void Main(string[] args)
        {
            ThreadSample sample = new ThreadSample(5);
 
            Thread t1 = new Thread(sample.CountNumbers);
            t1.Name = "ThreadOne";
            t1.Start();
            t1.Join();
            Console.WriteLine("--------------------------");
 
            Thread t2 = new Thread(Count);
            t2.Name = "ThreadTwo";
            t2.Start(3);
            t2.Join();
            Console.WriteLine("--------------------------");
 
            //使用lambda表达式引用另一个C#对象的方式被称为闭包。当在lambda表达式中使用任何局部变量时,C#会生成一个类,并将该变量作为该类的一个属性,但是我们无须定义该类,C#编译器会自动帮我们实现
            Thread t3 = new Thread(()=> CountNumbers(5));
            t3.Name = "ThreadThree";
            t3.Start();
            t3.Join();
            Console.WriteLine("--------------------------");
 
            int i = 10;
            Thread t4 = new Thread(() => PrintNumber(i));
            
            i = 20;
            Thread t5 = new Thread(() => PrintNumber(i));
            t4.Start();
            t5.Start();
            //t4, t5都会输出20, 因为t4,t5没有Start之前i已经变成20了
            Console.ReadKey();
        }
 
        static void Count(object iterations)
        {
            CountNumbers((int)iterations);
        }
 
        static void CountNumbers(int iterations)
        {
            for (int i = 1; i <= iterations; i++)
            {
                Thread.Sleep(TimeSpan.FromSeconds(0.5));
                Console.WriteLine("{0} prints {1}", Thread.CurrentThread.Name,i);
            }
        }
 
        static void PrintNumber(int number)
        {
            Console.WriteLine(number);
        }
    }
 
    class ThreadSample
    {
        private readonly int _iteration;
 
        public ThreadSample(int iteration)
        {
            _iteration = iteration;
        }
 
        public void CountNumbers()
        {
            for (int i = 1; i <= _iteration; i++)
            {
                Thread.Sleep(TimeSpan.FromSeconds(0.5));
                Console.WriteLine("{0} prints {1}",Thread.CurrentThread.Name,i);
            }
        }
    }
}

在这里插入图片描述
注释:也可以使用ThreadStart传递参数

3.9使用C# 锁 lock 关键字

using System;
using System.Diagnostics;
using System.Threading;
 
namespace MulityThreadNote
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Incorrect Counter");
            Counter c1 = new Counter();
            var t1 = new Thread(() => TestCounter(c1));
            var t2 = new Thread(() => TestCounter(c1));
            var t3 = new Thread(() => TestCounter(c1));
            t1.Start();
            t2.Start();
            t3.Start();
            t1.Join();
            t2.Join();
            t3.Join();
            Console.WriteLine("Total Count: {0}",c1.Count);
            Console.WriteLine("------------------------");
 
            Console.WriteLine("Correct counter");
            CounterWithLock c2 = new CounterWithLock();
            t1 = new Thread(() => TestCounter(c2));
            t2 = new Thread(() => TestCounter(c2));
            t3 = new Thread(() => TestCounter(c2));
            t1.Start();
            t2.Start();
            t3.Start();
            t1.Join();
            t2.Join();
            t3.Join();
            Console.WriteLine("Total count:{0}",c2.Count);
            Console.ReadLine();
        }
 
        static void TestCounter(CounterBase c)
        {
            for (int i = 0; i < 100000; i++)
            {
                c.Increment();
                c.Decrement();
            }
        }
 
        class Counter : CounterBase
        {
            public int Count { get; private set; }
            public override void Decrement()
            {
                Count--;
            }
 
            public override void Increment()
            {
                Count++;
            }
        }
 
        class CounterWithLock : CounterBase
        {
            private readonly object _asyncRoot = new object();
            public int Count { get; private set; }
            public override void Decrement()
            {
                lock (_asyncRoot)
                {
                    Count--;
                }
            }
 
            public override void Increment()
            {
                lock (_asyncRoot)
                {
                    Count++;
                }
            }
        }
 
        abstract class CounterBase
        {
            public abstract void Increment();
 
            public abstract void Decrement();
        }
    }
 
    class ThreadSample
    {
        private readonly int _iteration;
 
        public ThreadSample(int iteration)
        {
            _iteration = iteration;
        }
 
        public void CountNumbers()
        {
            for (int i = 1; i <= _iteration; i++)
            {
                Thread.Sleep(TimeSpan.FromSeconds(0.5));
                Console.WriteLine("{0} prints {1}",Thread.CurrentThread.Name,i);
            }
        }
    }
}

在这里插入图片描述
注释:不加锁,得出的结果不确定,竞争条件下很容易出错。加锁使程序安规定执行,得出的结果是正确的,但是性能受到了影响

3.10 使用Monitor类锁定资源

using System;
using System.Diagnostics;
using System.Threading;
 
namespace MulityThreadNote
{
    class Program
    {
        static void Main(string[] args)
        {
            object lock1 = new object();
            object lock2 = new object();
            new Thread(() => LockTooMuch(lock1, lock2)).Start();
            lock (lock2)
            {
                Thread.Sleep(1000);
                Console.WriteLine("Monitor.TryEnter allows not to get stuck, returning false after a specified timeout is elapsed");
                //直接使用Monitor.TryEnter, 如果在第二个参数之前还未获取到lock保护的资源会返回false
                if (Monitor.TryEnter(lock1, TimeSpan.FromSeconds(5)))
                {
                    Console.WriteLine("Acquired a protected resource successfully");
                }
                else
                {
                    Console.WriteLine("Timeout acquiring a resource");
                }
            }
            new Thread(() => LockTooMuch(lock1, lock2)).Start();
            Console.WriteLine("-----------------------------");
            /* 下面代码会造成死锁
            lock (lock2)
            {
                Console.WriteLine("This will be a deadlock!");
                Thread.Sleep(1000);
                lock (lock1)
                {
                    Console.WriteLine("Acquired a protected resource successfully");
                }
            }
            */
        }
 
        static void LockTooMuch(object lock1, object lock2)
        {
            lock (lock1)
            {
                Thread.Sleep(1000);
                lock (lock2) ;
            }
        }
    }
}

注释:Monitor.TryEnter在指定的时间内尝试获取指定对象上的排他锁

3.11 处理异常

using System;
using System.Diagnostics;
using System.Threading;
 
namespace MulityThreadNote
{
    class Program
    {
        static void Main(string[] args)
        {
            Thread t = new Thread(FaultyThread);
            t.Start();
            t.Join();
            try
            {
                t = new Thread(BadFaultyThread);
                t.Start();
            }
            catch (Exception ex)
            {
                Console.WriteLine("We won't get here");
            }
        }
        static void BadFaultyThread()
        {
            Console.WriteLine("Starting a faulty thread.....");
            Thread.Sleep(TimeSpan.FromSeconds(2));
            //这个异常主线程无法捕捉到,因为是在子线程抛出的异常。需要在子线程中加入try...catch捕获异常
            throw new Exception("Boom!");
        }
        static void FaultyThread()
        {
            try
            {
                Console.WriteLine("Starting a faulty thread...");
                Thread.Sleep(TimeSpan.FromSeconds(1));
                throw new Exception("Boom");
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception handled: {0}", ex.Message);
            }
        }
    }
}

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值