基础拾遗----多线程

基础拾遗

基础拾遗------特性详解

基础拾遗------webservice详解

基础拾遗------redis详解

基础拾遗------反射详解

基础拾遗------委托、事件详解

基础拾遗------接口详解

基础拾遗------泛型详解

基础拾遗-----依赖注入

基础拾遗-----数据注解与验证

基础拾遗-----mongoDB操作

基础拾遗----RabbitMQ

基础拾遗---CLR

基础拾遗----多线程

前言

我们知道c# 程序是自上而下的,但有的时候部分程序使用时间较长比如下载文档什么的。这是就可以用到线程。线程可以理解为是程序的执行路径,每个线程都定义了一个独特的控制流。如果您的应用程序涉及到复杂的和耗时的操作,那么设置不同的线程执行路径往往是有益的,每个线程执行特定的工作。

1.线程的生命周期

线程生命周期开始于 System.Threading.Thread 类的对象被创建时,结束于线程被终止或完成执行时。

下面列出了线程生命周期中的各种状态:

未启动状态:当线程实例被创建但 Start 方法未被调用时的状况。

就绪状态:当线程准备好运行并等待 CPU 周期时的状况。

不可运行状态:下面的几种情况下线程是不可运行的:

  已经调用 Sleep 方法

  已经调用 Wait 方法

  通过 I/O 操作阻塞

死亡状态:当线程已完成执行或已中止时的状况。

2.多线程的优缺点

2.1.优点

  1. 可以使用线程将代码同其他代码隔离,提高应用程序的可靠性。
  2. 可以使用线程来简化编码。
  3. 可以使用线程来实现并发执行。
  4. 可以提高CPU的利用率

2.2.缺点

  1. 线程开的越多,内存占用越大。
  2. 协调和管理代码的难度加大,需要CPU时间跟踪线程。
  3. 线程之间对共享资源的访问会相互影响,必须解决竞用共享资源的问题。
  4. 销毁线程需要了解可能发生的问题并对那些问题进行处理。

3.线程的实现

3.1.异步委托

关于委托基础拾遗------委托、事件详解这有详细介绍,我们都知道调用委托Delegate()或者Delegate?.Invoke()。进行执行,但是主线程的代码是从上至下进行执行的,那么我们想要委托方法进行一个新的线程只需BeginInvoke生成异步方法调用即可。

3.3.1.实现

        public delegate void ThreadDelegate();
        static void MethodDelegata()
        {
            Console.WriteLine("MethodDelegata");
        }
        static void Main(string[] args)
        {
            ThreadDelegate d = MethodDelegata;
            //BeginInvoke 两个参数一个是执行完后回调方法,一个是返回结果,如委托有参数载气前方添加即可。
            d.BeginInvoke(null,null);
            Console.WriteLine("Main");
            Console.ReadKey();
        } 

执行结果如下

 3.1.1.获取线程返回值

线程执行时有可能执行时间过长,如果我们要获取线程的返回值,这是就需要不回线程的状态和利用线程的回调方法。

  • 检测等待线程状态
       public delegate int ThreadDelegate(int i);
        static int MethodDelegata(int i)
        {
            Console.WriteLine("MethodDelegata" + i);
            Thread.Sleep(1000);
            return 100;

        }
        static void Main(string[] args)
        {
            ThreadDelegate d = MethodDelegata;
            //BeginInvoke 两个参数一个是执行完后回调方法,一个是返回结果
            IAsyncResult ar = d?.BeginInvoke(1, null, null);//获取线程执行状态
            Console.WriteLine("Main");
            while (!ar.IsCompleted) {//线程是否已执行完成,未完成执行
                Console.WriteLine(".");
                Thread.Sleep(10);//减少线程监测频率
            }
            int res = d.EndInvoke(ar);//获取线程的返回值
            Console.WriteLine(res);
            Console.ReadKey();
        }

结果如下

·  

我们如果不用while 的方式去等待方法执行结束,可以  ar.AsyncWaitHandle.WaitOne(1000); 但我们预估执行时间如果小于实际执行时间的化,返回值就获取不到了。

 bool isEnd = ar.AsyncWaitHandle.WaitOne(1000);
                if (isEnd)
            {
                int res = d.EndInvoke(ar);//获取线程的返回值
                Console.WriteLine(res);

            }
View Code
  • 利用 d?.BeginInvoke(1, callBackobject) 回调方法
       public delegate int ThreadDelegate(int i);
        static int MethodDelegata(int i)
        {
            Console.WriteLine("MethodDelegata" + i);
            Thread.Sleep(1000);
            return 100;

        }
        static void Main(string[] args)
        {
            ThreadDelegate d = MethodDelegata;
            //BeginInvoke 两个参数一个是执行完后回调方法,一个是返回结果
            IAsyncResult ar = d?.BeginInvoke(1, CallBack, d);//获取线程执行状态
            Console.WriteLine("Main");
         
            Console.ReadKey();
        }
        /// <summary>
        /// 结束回调方法
        /// </summary>
        /// <param name="ar"></param>
        private static void CallBack(IAsyncResult ar)
        {
            var obj=ar.AsyncState as ThreadDelegate;
            int res = obj.EndInvoke(ar);
            Console.WriteLine("线程结束,结果为:"+res);
        }

 我们通过lamda表达式优化一下上面的代码

     static void Main(string[] args)
        {
            ThreadDelegate d = MethodDelegata;
            //BeginInvoke 两个参数一个是执行完后回调方法,一个是返回结果
            //IAsyncResult ar = d?.BeginInvoke(1, CallBack, d);//获取线程执行状态
            d?.BeginInvoke(1, ar => {
                int res = d.EndInvoke(ar);
                Console.WriteLine("线程结束,结果为:" + res);
            }, null);
            Console.WriteLine("Main");
            Console.ReadKey();
        }

3.2.Thread 类

3.2.1.不带参数

        static void MethodThread()
        {
            Console.WriteLine("MethodDelegata");//第二个参数最多执行时间
            Thread.Sleep(1000);
        }
        static void Main(string[] args)
        {
            Thread t = new Thread(MethodThread);//创建了thread 对象单位启动
        //Thread t = new Thread(()=> { Console.WriteLine("MethodDelegata"); Thread.Sleep(1000);});//可直接用lamda表达式 
            t.Start();
            Console.WriteLine("Main");
            Console.ReadKey();
        }

3.2.2.带参数 

Start(obj) 传参:定义方法如果有参数必须object
        static void MethodThread(object s)
        {
            Console.WriteLine("MethodDelegata");
            Thread.Sleep(1000);

        }
        static void Main(string[] args)
        {
            //创建了thread 对象单位启动
            Thread t = new Thread(MethodThread);
            t.Start("wokao");//传递参数
            Console.WriteLine("Main");
            Console.ReadKey();
        }
对象传参:定义存放数据和线程方法的类
   class Program
    {
       
        static void Main(string[] args)
        {
            //创建了thread 对象单位启动
            ClassThead c = new ClassThead("1");
            Thread t = new Thread(c.MethodThread);
            t.Start();//传递参数
            Console.WriteLine("Main");
            Console.ReadKey();
        }

    }
    public class ClassThead
    {
        private string wr;
        public ClassThead(string s)
        {
            this.wr = s;
        }
        public  void MethodThread()
        {
            Console.WriteLine("MethodDelegata");
            Thread.Sleep(1000);

        }
    }

3.2.3 前台线程和后台线程

  1.     前台线程:只要存在有一个前台线程在运行,应用程序就在运行。
  2.     后台线程:应用程序关闭时,如果后台线程没有执行完,会被强制性的关闭
  3.     默认情况下,用Thread类创建的线程是前台线程,线程池中的线程总是后台线程。
  4. thread.IsBackground = true; 设置为后台程序
   static void MethodThread()
        {
            Console.WriteLine("MethodDelegata");
            Thread.Sleep(10000);

            Console.ReadKey();

        }
        static void Main(string[] args)
        {
            Thread t = new Thread(MethodThread);
            t.IsBackground = true;//当main执行结束后,不管t是否执行结束程序都关闭
            t.Start();//传递参数
            Console.WriteLine("Main");
        }

thread.Abort() 终止线程的执行。调用这个方法,会抛出一个ThreadAbortException类型的异常。

thread.Join() 将当前线程睡眠,等待thread线程执行完,然后再继续执行当前线程。

3.3.线程池threadPool

上面已经说了线程是为后台线程,在这多线程的操作推荐使用线程池线程而非新建线程。因为就算只是单纯的新建一个线程,这个线程什么事情也不做,都大约需要1M的内存空间来存储执行上下文数据结构,并且线程的创建与回收也需要消耗资源,耗费时间。而线程池的优势在于线程池中的线程是根据需要创建与销毁,是最优的存在。但是这也有个问题,那就是线程池线程都是后台线程,主线程执行完毕后,不会等待后台线程而直接结束程序。

 //如果带参数必须为object
        static void MethodThreadPool(object obj)
        {
            Console.WriteLine("MethodDelegata"+Thread.CurrentThread.ManagedThreadId);//当前线程id
            Thread.Sleep(1000);
         }
        static void Main(string[] args)
        {
            ThreadPool.QueueUserWorkItem(MethodThreadPool);// 必须带参数
            ThreadPool.QueueUserWorkItem(MethodThreadPool);
            ThreadPool.QueueUserWorkItem(MethodThreadPool);
            ThreadPool.QueueUserWorkItem(MethodThreadPool);
            ThreadPool.QueueUserWorkItem(MethodThreadPool);
            Console.WriteLine("Main");
            Console.ReadKey();
        }

3.4. Task

  1. Task是架构在Thread之上的,也就是说任务最终还是要抛给线程去执行。
  2. Task跟Thread不是一对一的关系,比如开10个任务并不是说会开10个线程,这一点任务有点类似线程池,但是任务相比线程池有很小的开销和精确的控制
  3. 可以将任务入队到线程池中异步执行。
  4. 线程池入队的任务无法取消
  5. 没有回调方法,可以使用委托实现回调
  6. Task在线程池的基础上进行了优化,所以它是后台线程

3.4.1.任务的定义

方法1

var t1 = new Task(() => TaskMethod("Task 1"));
t1.Start();
Task.WaitAll(t1);//等待所有任务结束 

方法2

Task.Run(() => TaskMethod("Task 2"));

方法3

  Task.Factory.StartNew(() => TaskMethod("Task 3")); 直接异步的方法 
  //或者
  var t3=Task.Factory.StartNew(() => TaskMethod("Task 3"));
  Task.WaitAll(t3);//等待所有任务结束

案列

 static void Main(string[] args)
        {
            var t1 = new Task(() => TaskMethod("Task 1"));
            var t2 = new Task(() => TaskMethod("Task 2"));
            t2.Start();
            t1.Start();
            Task.WaitAll(t1, t2);
            Task.Run(() => TaskMethod("Task 3"));
            Task.Factory.StartNew(() => TaskMethod("Task 4"));
            //标记为长时间运行任务,则任务不会使用线程池,而在单独的线程中运行。
            Task.Factory.StartNew(() => TaskMethod("Task 5"), TaskCreationOptions.LongRunning);

            Console.WriteLine("主线程执行业务处理.");
            //创建任务
            Task task = new Task(() =>
            {
                Console.WriteLine("使用System.Threading.Tasks.Task执行异步操作.");
                for (int i = 0; i < 10; i++)
                {
                    Console.WriteLine(i);
                }
            });
            //启动任务,并安排到当前任务队列线程中执行任务(System.Threading.Tasks.TaskScheduler)
            task.Start();
            Console.WriteLine("主线程执行其他处理");
            task.Wait();
            Thread.Sleep(TimeSpan.FromSeconds(1));
            Console.ReadLine();
        }

        static void TaskMethod(string name)
        {
            Console.WriteLine("Task {0} is running on a thread id {1}. Is thread pool thread: {2}",
                name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
        }
View Code

3.4.2.async/await

async是contextual关键字,await是运算符关键字。此用法是针对耗时时间较久的处理函数。异步编程不会影响线程,等待await执行其结果

async/await 结构可分成三部分:

  1. 调用方法:该方法调用异步方法,然后在异步方法执行其任务的时候继续执行;
  2. 异步方法:该方法异步执行工作,然后立刻返回到调用方法;
  3. await 表达式:用于异步方法内部,指出需要异步执行的任务。一个异步方法可以包含多个 await 表达式(不存在 await 表达式的话 IDE 会发出警告)。
class Program
    {
        async static void AsyncFunction()
        {
            await Task.Delay(1);
            Console.WriteLine("使用System.Threading.Tasks.Task执行异步操作.");
            for (int i = 0; i < 10; i++)
            {
                Console.WriteLine(string.Format("AsyncFunction:i={0}", i));
            }
        }
        public static void Main()
        {
            Console.WriteLine("主线程执行业务处理.");
            AsyncFunction();
            Console.WriteLine("主线程执行其他处理");
            for (int i = 0; i < 10; i++)
            {
                Console.WriteLine(string.Format("Main:i={0}", i));
            }
            Console.ReadLine();
        }
    }

async 3种写法

public async void getDate1()
{
}
public async Task getDate2()
{
     await Task.Delay(10000);
}
public async Task<T> getDate3()
{
    await Task.Delay(10000);
    return "";
}

4.线程争用与死锁

  class Program
    {
        static void ChangeState(object obj)
        {
            ClassThead c = obj as ClassThead;
            while (true)
            {
                c.MethodThread();
            }
        }
        //如果带参数必须为object

        static void Main(string[] args)
        {
            ClassThead c = new ClassThead();
            Thread t = new Thread(ChangeState);
            t.Start(c);
            Console.WriteLine("Main");
            Console.ReadKey();
        }

    }
    public class ClassThead
    {
        private int state = 6;
        public void MethodThread()
        {
            state++;
            if (state == 6)
            {
                Console.WriteLine("MethodDelegata");
                Thread.Sleep(1000);
            }
            state = 6;
        }
    }
View Code

可以从上面的方法中看到执行结果为空,虽然他在执行但是state一直都是>6的。所以是不执行的。

但如果开启两个线程的结果是什么呢?

是执行的因为多个线程有可能是在执行时另一个线程给他赋值了。所以我们就要给对象加锁

        static void ChangeState(object obj)
        {
            ClassThead c = obj as ClassThead;
            while (true)
            {
                lock (c)
                {
                    c.MethodThread();
                }
            }
        }

注:但是有可能会出现线程争用一直等待的情况,所以在编程过程设计好锁的顺序

原文地址:https://www.cnblogs.com/wyl1924/p/13162855.html