系统相关
首页 > 系统相关> > 进程-线程-多线程-异步

进程-线程-多线程-异步

作者:互联网

进程-线程-多线程-异步

一、多线程的本质

1、并发多线程的启动、结束顺序

并发线程的启动是无序。
执行相同代码的并发线程的运行时间也不相同,所以结束时间也不同。

a、如何控制多线程的调用顺序

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

namespace Thread_Task_Async
{

    public class MoreThread
    {
        public void DoSometing(string name)
        {
            Console.WriteLine($"{name}____{Thread.CurrentThread.ManagedThreadId}");
        }

        public void Do()
        {
            Console.WriteLine($"主线程开始");
            Action<string> action = DoSometing;//无返回值的委托
            IAsyncResult asyncResult = null;//这是BeginInvoke的返回值类型。
            AsyncCallback callback = e =>  //这是回调函数
            {
                Console.WriteLine(object.ReferenceEquals(asyncResult, e));
                Console.WriteLine(e.AsyncState);
                Console.WriteLine($"回调函数完成   {Thread.CurrentThread.ManagedThreadId}");
            };
            //BeginInvoke是异步调用委托,callback是回调函数。
            //BeginInvoke的返回值是回调函数中的参数
            //第三个参数是object类型,它是BeginInvoke的返回值中的字段AsyncState,可以用它传递任何你想给回调函数传的东西。
            asyncResult = action.BeginInvoke("Do", callback, "xxxx");
            Console.WriteLine($"主线程完成");
        }
    }
}

b、阻塞主线程等待子线程

二、异步操作的本质

所有的程序最终都会由计算机硬件来执行,所以为了更好的理解异步操作的本质,我们有必要了解一下它的硬件基础。 熟悉电脑硬件的朋友肯定对DMA这个词不陌生,硬盘、光驱的技术规格中都有明确DMA的模式指标,其实网卡、声卡、显卡也是有DMA功能的。DMA就是直接内存访问的意思,也就是说,拥有DMA功能的硬件在和内存进行数据交换的时候可以不消耗CPU资源。只要CPU在发起数据传输时发送一个指令,硬件就开始自己和内存交换数据,在传输完成之后硬件会触发一个中断来通知操作完成。这些无须消耗CPU时间的I/O操作正是异步操作的硬件基础。所以即使在DOS这样的单进程(而且无线程概念)系统中也同样可以发起异步的DMA操作。

三、线程

1、Thread

2、ThreadPool

3、Task

 private void button2_Click(object sender, EventArgs e)
        {
            Console.WriteLine("22222222222222222");
            List<int> list = new List<int>();
            for(int i =0; i<10000;i++)
            {
                list.Add(i);
            }
            Action<int> action = i =>
            {
                Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId}");
                Thread.Sleep(new Random(i).Next(100, 300));
            };
            List<Task> taskList = new List<Task>();
            foreach(var i in list)
            {
                var k = i;
                taskList.Add(Task.Run(() => action.Invoke(k)));
                if(taskList.Count > 10)
                {
                    Task.WaitAny(taskList.ToArray());
                    taskList = taskList.Where(t => t.Status != TaskStatus.RanToCompletion).ToList();
                }
            }
            Task.WhenAll(taskList.ToArray());
            Console.WriteLine("22222222222222222");
        }

4、TaskFactory

5、Parallel

private void button3_Click(object sender, EventArgs e)
        {
            ParallelOptions parallelOptions = new ParallelOptions();
            parallelOptions.MaxDegreeOfParallelism = 3;
            Parallel.For(0, 40, parallelOptions, (i, state) =>
            {
                Console.WriteLine($"111111111111111111111111111111----{Thread.CurrentThread.ManagedThreadId}");
                //if (i == 2)
                //{
                //    Console.WriteLine("线程取消,当前任务结束");
                //    state.Break();//当前这次线程结束
                //    return;//有return代表此代码之后的操作不执行。 
                //}
                if (i == 20)
                {
                    Console.WriteLine("线程取消,当前任务结束");
                    state.Stop();   //  结束Parallel所有线程
                    return;//有return代表此代码之后的操作不执行。 
                }
                Console.WriteLine($"22222222222222222222222222----{Thread.CurrentThread.ManagedThreadId}");
            });
        }

6、async await 语法糖

四、异常处理

五、线程取消

private void button4_Click(object sender, EventArgs e)
        {
            try
            {
                List<Task> taskList = new List<Task>();
                TaskFactory taskFactory = new TaskFactory();
                CancellationTokenSource cts = new CancellationTokenSource();
                for (int i = 0; i < 40; i++)
                {
                    string name = String.Format($"this my work{i}");
                    Action<object> act = t =>
                    {
                        try
                        {
                            Thread.Sleep(2000);
                            if(t.ToString().Equals("this my work11"))
                            {
                                throw new Exception(string.Format($"{t} 执行失败"));
                            }
                            if (t.ToString().Equals("this my work12"))
                            {
                                throw new Exception(string.Format($"{t} 执行失败"));
                            }
                            if (cts.IsCancellationRequested)
                            {
                                Console.WriteLine($"{t}放弃执行");
                                return;
                            }
                            else
                            {
                                Console.WriteLine($"{t}执行成功");
                            }
                        }
                        catch (Exception ex)
                        {
                            cts.Cancel();
                            Console.WriteLine(ex.Message);
                        }
                    };
                    taskList.Add(taskFactory.StartNew(act, name, cts.Token));
                }
                Task.WaitAll(taskList.ToArray());
            }
            catch(AggregateException ae)
            {
                foreach(var item in ae.InnerExceptions)
                {
                    Console.WriteLine(item.Message);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

六、线程安全

private void button5_Click(object sender, EventArgs e)
        {
            #region 此代码存在线程安全问题
            TaskFactory taskFactory = new TaskFactory();
            List<Task> taskList = new List<Task>();
            int TotalCount = 0;
            List<int> intList = new List<int>();
            for (int i = 0; i < 1000; i++)
            {
                int newI = i;
                taskList.Add(taskFactory.StartNew(() =>
                {
                    TotalCount += 1;//TotalCount是公共资源,多线程用此资源存在问题(有可能两个线程同时获取到此资源进行+1,这样就少加了一个1).
                    intList.Add(newI);//这个同理
                }));
            }
            Task.WaitAll(taskList.ToArray());
            Console.WriteLine($"{TotalCount}");
            Console.WriteLine($"{taskList.Count}");
            Console.WriteLine($"{intList.Count}"); 
            #endregion
        }
  1. 用的时候注意尽量缩小lock的范围。
  2. 声明锁,必须是引用类型,值类型无效, 用private可以保证其他类不会锁此资源,static保证全局唯一性(也可以不声明)。
  3. 锁不可以声明为string类型,虽然它是引用类型,但是因为它用了享元模式。
private static readonly object ClickLock = new object();//这是微软推荐的锁的写法。
private void button5_Click(object sender, EventArgs e)
        {
            #region lock解决
            {
                TaskFactory taskFactory = new TaskFactory();
                List<Task> taskList = new List<Task>();
                int TotalCount = 0;
                List<int> intList = new List<int>();
                for (int i = 0; i < 1000; i++)
                {
                    int newI = i;
                    taskList.Add(taskFactory.StartNew(() =>
                    {
                        lock (ClickLock)
                        {
                            TotalCount += 1;
                            intList.Add(newI);
                        }
                        
                    }));
                }
                Task.WaitAll(taskList.ToArray());
                Console.WriteLine($"{TotalCount}");
                Console.WriteLine($"{taskList.Count}");
                Console.WriteLine($"{intList.Count}");
            }
            #endregion
        }

第二种: 安全队列 ConcurrentQueue,用一个线程去完成操作(不止单线程,还用了其他技术)。
第三种: 使用多线程,注意不要冲突,进行数据拆分,避免冲突。也是最推荐的一种。

Core松 发布了8 篇原创文章 · 获赞 0 · 访问量 110 私信 关注

标签:异步,Task,Console,Thread,taskList,线程,WriteLine,多线程
来源: https://blog.csdn.net/qq_41807126/article/details/104288894