异步多线程 笔记

/// 同步异步:
/// 同步方法:发起调用,完成后才继续下一行;非常符合开发思维,有序执行;
/// 诚心诚意的请人吃饭,邀请Nick,Nick要忙一会儿,等着Nick完成后,再一起去吃饭
/// 异步方法:发起调用,不等待完成,直接进入下一行,启动一个新线程来完成方法的计算
/// 客气一下的请人吃饭,邀请亡五,亡五要忙一会儿,你忙着我去吃饭了,你忙完自己去吃饭吧

 

1.使用 action 执行多线程 示例

//代码示例
Action<string> action = this.DoSomethingLong;
action.Invoke("btnAsync_Click_1"); //同步执行
action.BeginInvoke("btnAsync_Click_1", null, null); //新起线程异步执行
//1 回调:将后续动作通过回调参数传递进去,子线程完成计算后,去调用这个回调委托
IAsyncResult asyncResult = null;//是对异步调用操作的描述
AsyncCallback callback = ar =>
{
Console.WriteLine($"{object.ReferenceEquals(ar, asyncResult)}");
Console.WriteLine($"btnAsyncAdvanced_Click计算成功了。{ar.AsyncState}。{Thread.CurrentThread.ManagedThreadId.ToString("00")}");
};
asyncResult = action.BeginInvoke("btnAsyncAdvanced_Click", callback, "花生"); //会传进去  花生 =ar.AsyncState

//2 通过IsComplate等待,卡界面--主线程在等待,边等待边提示
int i = 0;
while (!asyncResult.IsCompleted)  //asyncResult.IsCompleted 是否已完成本次任务
{
if (i < 9)
{
Console.WriteLine($"中华民族复兴完成{++i * 10}%....");
}
else
{
Console.WriteLine($"中华民族复兴完成99.999999%....");
}
Thread.Sleep(200);
}
Console.WriteLine("中华民族复兴已完成,沉睡的东方雄狮已觉醒!");
//3 WaitOne等待,即时等待 限时等待
asyncResult.AsyncWaitHandle.WaitOne();//直接等待任务完成
asyncResult.AsyncWaitHandle.WaitOne(-1);//一直等待任务完成
asyncResult.AsyncWaitHandle.WaitOne(1000);//最多等待1000ms,超时就不等了

 

//4 EndInvoke 即时等待,而且可以获取委托的返回值 一个异步操作只能End一次
Func<int> func = () =>
{
Thread.Sleep(2000);
return DateTime.Now.Hour;
};
int iResult = func.Invoke();//22
IAsyncResult asyncResult = func.BeginInvoke(ar =>
{
//int iEndResultIn = func.EndInvoke(ar);
}, null);
int iEndResult = func.EndInvoke(asyncResult);//22

 

2.Thread 执行多线程 示例

//启动线程
ParameterizedThreadStart method=o=>this.DoSomethingLong("btnThread_Click");
Thread thread = new Thread(method);
thread.Start("123");//开启线程,执行委托的内容
ThreadStart method = () =>
{
    Thread.Sleep(5000);
    this.DoSomethingLong("btnThread_Click");
    Thread.Sleep(5000);
};


//线程操作
Thread thread = new Thread(method);
thread.Start();//开启线程,执行委托的内容
thread.Suspend();//暂停
thread.Resume();//恢复    真的不该要的,暂停不一定马上暂停;让线程操作太复杂了
thread.Abort(); //线程销毁,线程是计算机资源,程序想停下线程,只能向操作系统通知(线程抛异常),会有延时/不一定能真的停下来
Thread.ResetAbort(); //线程恢复销毁
//1等待 while (thread.ThreadState != ThreadState.Stopped) { Thread.Sleep(200);//当前线程休息200ms } //2Join等待 thread.Join();//运行这句代码的线程,等待thread的完成 thread.Join(1000);//最多等待1000ms //优先执行 thread.Priority = ThreadPriority.Highest;//最高优先级:优先执行,但不代表优先完成 甚至说极端情况下,还有意外发生,不能通过这个来控制线程的执行先后顺序 //前台线程 thread.IsBackground = false;//默认是false 前台线程,进程关闭,线程需要计算完后才退出,true 关闭进程,线程退出

 基于thread封装一个回调,回调:启动子线程执行动作A--不阻塞--A执行完后子线程会执行动作B

//不带返回值的封装

private void ThreadWithCallBack(ThreadStart threadStart, Action actionCallback)
{
 ThreadStart method = new ThreadStart(() =>
 {
    threadStart.Invoke();
actionCallback.Invoke(); });
new Thread(method).Start(); }
ThreadWithCallBack(threadStart, actionCallBack);
//带返回值的封装 异步,非阻塞的 ,还能获取到最终计算结果
 private Func<T> ThreadWithReturn<T>(Func<T> func)
        {
            T t = default(T);
            ThreadStart threadStart = new ThreadStart(() =>
            {
                t = func.Invoke();
            });
            Thread thread = new Thread(threadStart);
            thread.Start();

            return new Func<T>(() =>
            {
                thread.Join();
                //thread.ThreadState
                return t;
            });
        }

Func<int> funcThread = this.ThreadWithReturn(func);//非阻塞
Console.WriteLine("do something 1");
Console.WriteLine("do something 2");
Console.WriteLine("do something 3");
int iResult = funcThread.Invoke();//阻塞

 

3.ThreadPool 的应用

//启动
ThreadPool.QueueUserWorkItem(o => this.DoSomethingLong("btnThreadPool_Click1"));
ThreadPool.QueueUserWorkItem(o => this.DoSomethingLong("btnThreadPool_Click2"), "昔梦");

//当前电脑最大workerThreads
ThreadPool.GetMaxThreads(out int workerThreads, out int completionPortThreads);
当前电脑最小workerThreads
ThreadPool.GetMinThreads(out int workerThreadsMin, out int completionPortThreadsMin);


//设置的线程池数量是进程全局的,委托异步调用--Task--Parrallel--async/await 全部都是线程池的线程,直接new Thread不受这个数量限制的(但是会占用线程池的线程数量)
ThreadPool.SetMaxThreads(8, 8);//设置的最大值,必须大于CPU核数,否则设置无效
ThreadPool.SetMinThreads(2, 2);



//等待
ManualResetEvent mre = new ManualResetEvent(false);
//false---关闭---Set打开---true---WaitOne就能通过
//true---打开--ReSet关闭---false--WaitOne就只能等待
ThreadPool.QueueUserWorkItem(o =>
 {
      this.DoSomethingLong("btnThreadPool_Click1");
      mre.Set();
 });
Console.WriteLine("Do Something else...");
Console.WriteLine("Do Something else...");
Console.WriteLine("Do Something else...");
mre.WaitOne();
Console.WriteLine("任务已经完成了。。。");

 

4.Task    .NET3.0推出

Thread.Sleep(2000);//同步等待--当前线程等待2s 然后继续
Task.Delay(2000); //异步等待,一般结合后续其他方法使用
如:
        Task task = Task.Delay(2000) .ContinueWith(t =>
            {
               stopwatch.Stop();
               Console.WriteLine($"Delay耗时{stopwatch.ElapsedMilliseconds}");
               Console.WriteLine($"This is ThreadId={Thread.CurrentThread.ManagedThreadId.ToString ("00")}");
 });//异步等待--等待2s后启动新任务


TaskFactory
//开发可以多人合作---多线程--提升性能
TaskFactory taskFactory = new TaskFactory();
List<Task> taskList = new List<Task>();
taskList.Add(taskFactory.StartNew(() => this.Coding("冰封的心", "Portal")));
taskList.Add(taskFactory.StartNew(() => this.Coding("随心随缘", "  DBA ")));
taskList.Add(taskFactory.StartNew(() => this.Coding("心如迷醉", "Client")));
taskList.Add(taskFactory.StartNew(() => this.Coding(" 千年虫", "BackService")));
taskList.Add(taskFactory.StartNew(() => this.Coding("简单生活", "Wechat")));


 //谁第一个完成,获取一个红包奖励
taskFactory.ContinueWhenAny(taskList.ToArray(), t => Console.WriteLine($"XXX开发完成,获取个红包奖励{Thread.CurrentThread.ManagedThreadId.ToString("00")}"));

 //全部作业完成后,一起庆祝一下
taskFactory.ContinueWhenAll(taskList.ToArray(), rArray => Console.WriteLine($"开发都完成,一起庆祝一下{Thread.CurrentThread.ManagedThreadId.ToString("00")}"))

//全部作业完成后,一起庆祝一下
taskFactory.ContinueWhenAll(taskList.ToArray(), rArray => Console.WriteLine($"开发都完成,一起庆祝一下{Thread.CurrentThread.ManagedThreadId.ToString("00")}"))

 //这里是为了保证 全部作业完成后,一起庆祝一下 ,这里将在前面5个任务执行完之后再执行。
 taskList.Add(taskFactory.ContinueWhenAll(taskList.ToArray(), rArray => Console.WriteLine($"开发都完成,一起庆祝一下{Thread.CurrentThread.ManagedThreadId.ToString("00")}")));
 //ContinueWhenAny  ContinueWhenAll 非阻塞式的回调;而且使用的线程可能是新线程,也可能是刚完成任务的线程,唯一不可能是主线程



 //阻塞当前线程,等着任意一个任务完成
Task.WaitAny(taskList.ToArray());//也可以限时等待
 Console.WriteLine("有一个模块已完成");

//需要能够等待全部线程完成任务再继续  阻塞当前线程,等着全部任务完成
Task.WaitAll(taskList.ToArray());
 Console.WriteLine("5个模块全部完成后");

//Task.WaitAny  WaitAll都是阻塞当前线程,等任务完成后执行操作
//阻塞卡界面,是为了并发以及顺序控制
//网站首页:A数据库 B接口 C分布式服务 D搜索引擎,适合多线程并发,都完成后才能返回给用户,需要等待WaitAll
//列表页:核心数据可能来自数据库/接口服务/分布式搜索引擎/缓存,多线程并发请求,哪个先完成就用哪个结果,其他的就不管了


扩展 ,假如说我想控制下Task的并发数量,该怎么做?  20个
List<Task> taskList = new List<Task>();
                for (int i = 0; i < 10000; i++)
                {
                    int k = i;
                    if (taskList.Count(t => t.Status != TaskStatus.RanToCompletion) >= 20)
                    {
                        Task.WaitAny(taskList.ToArray());
                        taskList = taskList.Where(t => t.Status != TaskStatus.RanToCompletion).ToList();
                    }
                    taskList.Add(Task.Run(() =>
                    {
                        Console.WriteLine($"This is {k} running ThreadId={Thread.CurrentThread.ManagedThreadId.ToString("00")}");
                        Thread.Sleep(2000);
                    }));
                }

 

5.Parallel

//Parallel并发执行多个Action 多线程的,
//主线程会参与计算---阻塞界面
//等于TaskWaitAll+主线程计算
Parallel.Invoke(() => this.DoSomethingLong("btnParallel_Click_1"),
                    () => this.DoSomethingLong("btnParallel_Click_2"),
                    () => this.DoSomethingLong("btnParallel_Click_3"),
                    () => this.DoSomethingLong("btnParallel_Click_4"),
                    () => this.DoSomethingLong("btnParallel_Click_5"));

或者
Parallel.For(0, 5, i => this.DoSomethingLong($"btnParallel_Click_{i}"));
或者
Parallel.ForEach(new int[] { 0, 1, 2, 3, 4 }, i => this.DoSomethingLong($"btnParallel_Click_{i}"));

使用控制并发线程数量,这里会阻塞主线程
ParallelOptions options = new ParallelOptions();
options.MaxDegreeOfParallelism = 3;
Parallel.For(0, 10, options, i => this.DoSomethingLong($"btnParallel_Click_{i}"));

 //有没有办法不阻塞?
Task.Run(() =>
{
   ParallelOptions options = new ParallelOptions();
   options.MaxDegreeOfParallelism = 3;
   Parallel.For(0, 10, options, i => this.DoSomethingLong($"btnParallel_Click_{i}"));
});

 

posted @ 2020-03-10 16:01  爱如少年。  阅读(207)  评论(0编辑  收藏  举报