zoukankan      html  css  js  c++  java
  • 基础拾遗----多线程

    基础拾遗

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

    基础拾遗------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();
                    }
                }
            }

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

  • 相关阅读:
    【spring 事务注解配置】事务回滚
    myisam 和 innodb 对比
    mysql replace into用法详细说明
    [nginx] 配置
    【lucene】中文分词
    Lucene 4.9 document的简单应用
    Spring事务配置的五种方式
    Open-Drain与Push-Pull
    HDMI中的AVmute是什么功能
    在Keil uv5里面添加STC元器件库,不影响其他元件
  • 原文地址:https://www.cnblogs.com/wyl1924/p/13162855.html
Copyright © 2011-2022 走看看