今天继续讲讲线程的异常处理、线程取消、多线程的临时变量和线程安全lock的问题。

1、异步处理。

一般来说如果是同步方法的异步处理,我们大多都是try catch住,但是异步方法应该怎么做呢。

    #region 异常处理

                //多线程的委托是不允许异常的, try catch包住,写下日志
for (int i = 0; i < 20; i++)
{
string name = string.Format("btnThreadCore_Click{0}", i);
Action<object> act = t =>
{
try
{
Thread.Sleep(2000);
if (t.ToString().Equals("btnThreadCore_Click11"))
{
throw new Exception(string.Format("{0} 执行失败", t));
}
if (t.ToString().Equals("btnThreadCore_Click12"))
{
throw new Exception(string.Format("{0} 执行失败", t));
}
Console.WriteLine("{0} 执行成功", t);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
};
taskList.Add(taskFactory.StartNew(act, name));
}
Task.WaitAll(taskList.ToArray());
#endregion

  

2、线程取消。

Task不能主动取消,就好比向CPU发起了一个请求,但是你中途想中断这个请求,在正常情况下是做不到的,

同样,线程也做不到这一点,只有通过检测信号量的方式,来检测,使其线程本身来做。

  CancellationTokenSource cts = new CancellationTokenSource();

                for (int i = 0; i < 40; i++)
{
string name = string.Format("btnThreadCore_Click{0}", i);
Action<object> act = t =>
{
try
{ Thread.Sleep(2000);
if (t.ToString().Equals("btnThreadCore_Click11"))
{
throw new Exception(string.Format("{0} 执行失败", t));
}
if (t.ToString().Equals("btnThreadCore_Click12"))
{
throw new Exception(string.Format("{0} 执行失败", t));
}
if (cts.IsCancellationRequested)
{
Console.WriteLine("{0} 放弃执行", t);
}
else
{
Console.WriteLine("{0} 执行成功", t);
}
}
catch (Exception ex)
{
cts.Cancel();
Console.WriteLine(ex.Message);
}
};
taskList.Add(taskFactory.StartNew(act, name);//没有启动的任务 在Cancel后放弃启动
}
Task.WaitAll(taskList.ToArray());

  

通过代码运行可以看到会出现三种结果,那么这三种结果是什么情况下出现的呢,

执行成功和执行失败这两种情况应该好理解,,放弃执行是在执行失败出现时,捕获住了异常信息,然后通过cts.Cancel();使信号量改变,

然后通过cts.IsCancellationRequested判断,这就出现了只要是出现了执行失败,后面都是放弃执行的情况。

3、多线程临时变量

           for (int i = 0; i < 5; i++)
{ new Action(() =>
{
//Thread.Sleep(100);
Console.WriteLine(i); }).BeginInvoke(null, null);
}

执行这么一段关键代码,会出现什么样的结果呢。

出现5个5,这是为什么呢,怎么和我们想的不一样,按理说不应该是出现0、1、2、3、4这样的数?

这里因为for循环是一定会比线程调用快,每一遍循环完,只是提交了线程,还没有调用,当调用时,循环已经结束,额调用时只会取最后i的值。

这就会出现5个5的情况,那么如何才能出现我们想要的结果呢。

                for (int i = 0; i < 5; i++)
{
int k = i;
new Action(() =>
{
Thread.Sleep(100);
// Console.WriteLine(i);
Console.WriteLine(k);
}).BeginInvoke(null, null);
}

只需要在循环体中加一个变量存储i的值,就可以了。

4、线程安全 lock

关于线程安全,有的人太过于重视,而也有的人一点也不关心。那么我们应该怎么做线程安全呢。

                private int TotalCount = 0;             
for (int i = 0; i < 10000; i++)
{ taskList.Add(taskFactory.StartNew(() =>
{
this.TotalCount += 1;
}));
}
Task.WaitAll(taskList.ToArray()); Console.WriteLine(this.TotalCount);

先来看看这段代码,可能大多数人会认为结果会是10000这样的结果,但是事实呢

你会发现,第一次是9997,第二次9998,第三次是9996,没有一次出现我们想要的结果。这又是为什么,

因为我们声明的private int TotalCount = 0,是共有变量,所有的线程都是调用同一个,这就出现了线程安全的问题。

那么我们应该如何解决这种情况呢,这就要加一把锁。

private static object btnThreadCore_Click_Lock = new object();

               for (int i = 0; i < 10000; i++)
{ taskList.Add(taskFactory.StartNew(() =>
{ lock (btnThreadCore_Click_Lock)
{
this.TotalCount += 1; }
}));
}
Task.WaitAll(taskList.ToArray()); Console.WriteLine(this.TotalCount);
 

这样在执行相加的时候只会允许一个线程进行相加。

讲完这四点,再来说说Await/Async,这两个一般都是同时出现

1、只出现Async,会出现一个警告,合普通线程没什么区别。

  private static async void NoReturnNoAwait()
{
//主线程执行
Console.WriteLine("NoReturnNoAwait Sleep before Task,ThreadId={0}", Thread.CurrentThread.ManagedThreadId);
Task task = Task.Run(() =>//启动新线程完成任务
{
Console.WriteLine("NoReturnNoAwait Sleep before,ThreadId={0}", Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(3000);
Console.WriteLine("NoReturnNoAwait Sleep after,ThreadId={0}", Thread.CurrentThread.ManagedThreadId);
}); //主线程执行
Console.WriteLine("NoReturnNoAwait Sleep after Task,ThreadId={0}", Thread.CurrentThread.ManagedThreadId);
}

  

同时出现

        private static async void NoReturn()
{
//主线程执行
Console.WriteLine("NoReturn Sleep before await,ThreadId={0}", Thread.CurrentThread.ManagedThreadId);
TaskFactory taskFactory = new TaskFactory();
Task task = taskFactory.StartNew(() =>
{
Console.WriteLine("NoReturn Sleep before,ThreadId={0}", Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(3000);
Console.WriteLine("NoReturn Sleep after,ThreadId={0}", Thread.CurrentThread.ManagedThreadId);
});
await task;
//子线程执行 其实是封装成委托,在task之后成为回调(编译器功能 状态机实现)
Console.WriteLine("NoReturn Sleep after await,ThreadId={0}", Thread.CurrentThread.ManagedThreadId);
}

  

带有await时,后面执行时,会发现也是子线程在执行。

   private static async Task NoReturnTask()
{
//这里还是主线程的id
Console.WriteLine("NoReturnTask Sleep before await,ThreadId={0}", Thread.CurrentThread.ManagedThreadId); Task task = Task.Run(() =>
{
Console.WriteLine("NoReturnTask Sleep before,ThreadId={0}", Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(3000);
Console.WriteLine("NoReturnTask Sleep after,ThreadId={0}", Thread.CurrentThread.ManagedThreadId);
});
await task;
Console.WriteLine("NoReturnTask Sleep after await,ThreadId={0}", Thread.CurrentThread.ManagedThreadId); //return new TaskFactory().StartNew(() => { }); 不能return
}

  这种和前面的没什么区别,只不过它带有返回值Task。其运行结果是一样的。

  private static async Task<long> SumAsync()
{
Console.WriteLine("SumAsync {1} start ManagedThreadId={0}", Thread.CurrentThread.ManagedThreadId, 111);
long result = 0; await Task.Run(() =>
{
for (int k = 0; k < 10; k++)
{
Console.WriteLine("SumAsync {1} await Task.Run ManagedThreadId={0}", Thread.CurrentThread.ManagedThreadId, k);
System.Threading.Thread.Sleep(1000);
} for (long i = 0; i < 999999999; i++)
{
result += i;
}
});
Console.WriteLine("SumAsync {1} end ManagedThreadId={0}", Thread.CurrentThread.ManagedThreadId, 111);
return result;
}

  如果是带有返回值的情况

        private static async Task<long> SumAsync()
{
Console.WriteLine("SumAsync {1} start ManagedThreadId={0}", Thread.CurrentThread.ManagedThreadId, 111);
long result = 0; await Task.Run(() =>
{ for (long i = 0; i < 999999999; i++)
{
result += i;
}
});
Console.WriteLine("SumAsync {1} end ManagedThreadId={0}", Thread.CurrentThread.ManagedThreadId, 111);
return result;
}

  

            Task<long> t = SumAsync();

            long lResult = t.Result;//访问result   主线程等待Task的完成
//t.Wait();//等价于上一行
Console.WriteLine(lResult);

  

Thread(线程)四的更多相关文章

  1. C#中的线程四(System.Threading.Thread)

    C#中的线程四(System.Threading.Thread) 1.最简单的多线程调用 System.Threading.Thread类构造方法接受一个ThreadStart委托,改委托不带参数,无 ...

  2. 学习接水系统(java+thread线程)

    (一)项目框架分析 对于学生并发接水项目,根据面向对象的思想,需要创建两个对象,即学生和水龙头. 接下来主要讲解不排队接水和排队接水两张情况. 项目的目录文件如下: (二)不排队接水 假设有四个学生小 ...

  3. java thread 线程锁同步,锁,通信

    12.线程同步 当多个线程访问同一个数据时,非常容易出现线程安全问题.这时候就需要用线程同步 Case:银行取钱问题,有以下步骤: A.用户输入账户.密码,系统判断是否登录成功 B.用户输入取款金额 ...

  4. QT5 Thread线程

    QT5 Thread线程继承QThread方式 一.首先分析一下 QTimer Class与 Sleep()函数之间的秘密 QTimer *t = new QTimer(*parent); //创建Q ...

  5. Thread线程join方法自我理解

    Thread线程join方法自我理解 thread.join():等待thread线程运行终止,指的是main-thread(main线程)必须等待thread线程运行结束,才能继续thread.jo ...

  6. java 并发(三)---Thread 线程

    Thread 的状态 线程共有五种状态.分别是: (1)新建 (2)就绪 (3)运行 (4)阻塞 (5)死亡 ,下面列列举的状态需要结合状态示意图更好理解.  新建状态(New): 新创建了一个线程对 ...

  7. c++11中关于`std::thread`线程传参的思考

    关于std::thread线程传参的思考 最重要要记住的一点是:参数要拷贝到线程独立内存中,不管是普通类型.还是引用类型. 对于传递参数是引用类型,需要注意: 1.当指向动态变量的指针(char *) ...

  8. Thread 线程池

    Thread 线程池: 当使用多个较短存活期的线程有利时,运用线程池技术可以发挥作用.运用这一技术时,不是为每个任务创建一个全新的线程,而可以从线程池中抽出线程,并分配给任务.当线程完成任务后,再把它 ...

  9. Thread线程的基础知识及常见疑惑点

    引言 相信各位道友在平时工作中已经很少直接用到Thread线程类了,现在大多是通过线程池或者一些多线程框架来操作线程任务,但我觉得还是有必要了解清楚Thread线程类中各种方法的含义,了解了底层才能更 ...

  10. Thread线程框架学习

    原文:https://www.cnblogs.com/wangkeqin/p/9351299.html Thread线程框架 线程定义:线程可以理解为一个特立独行的函数.其存在的意义,就是并行,避免了 ...

随机推荐

  1. phpcms v9 调用自定义字段多图片的第一张或第N张图为缩略图

    1.打开相应要使用组图的模型的组图字段,添加组图 字段提示为 <div class="content_attr"> <label><input typ ...

  2. java 读取excel

    1. 需要下载jxl.jar包 自己研究了一下,代码如下 package file;import java.io.File;import java.io.IOException;import java ...

  3. 去掉CI框架默认url中的index.php

    1:.htaccess //放置在根目录下,和入口文件index.php的同级目录<IfModule mod_rewrite.c>RewriteEngine onRewriteCond % ...

  4. Spring Web 配置文件加载路径问题

    Spring:   定位   载入   注册 我们常用的加载context文件的方法有如下三个: 1.FileSystemXmlApplicationContext 这个方法是从文件绝对路径加载配置文 ...

  5. GC机制总结

    一.为什么需要GC 应用程序对资源操作,通常简单分为以下几个步骤: 1.为对应的资源分配内存 2.初始化内存 3.使用资源 4.清理资源 5.释放内存 应用程序对资源(内存使用)管理的方式,常见的一般 ...

  6. Android混淆

    一.为什么要混淆 为了避免apk在发布后被用户通过反编译拿到源代码和资源文件,然后修改资源和代码之后就变成一个新的apk.而经过混淆后的APK,即使被反编译,也难以阅读,注意混淆不是让apk不能阅读, ...

  7. 14.如何解决使用webpack打包之后,font-awsome路径不对的问题,终极解决方法

    问题描述: 使用webpack打包vue项目,使用font-awsome字体,发现打包之后,font-awsome图标不显示,报错为路径不对 看了下打包的路径,的确路径不对,打包之后font-awso ...

  8. Java 9 揭秘(10. 模块API)

    Tips 做一个终身学习的人. 在本章节中,主要介绍以下内容: 什么是模块 API 如何在程序中表示模块和模块描述 如何读取程序中的模块描述 如何表示模块的版本 如何使用Module和ModuleDe ...

  9. Gulp安装流程、使用方法及cmd常用命令导览

    Gulp安装流程.使用方法及CMD常用命令导览 来自前端小白的gulp及周边知识学习总结 一.名词介绍: Npm--node包管理工具 一开始我不理解,包管理工具是什么鬼.后来用到的gulp也好,gu ...

  10. 30分钟学会使用Spring Web Services基础开发

    时隔一年终于又推出了一篇30分钟系列,上一篇<30分钟学会反向Ajax>是2016年7月的事情了.时光荏苒,岁月穿梭.虽然一直还在从事Java方面的开发工作,但是私下其实更喜欢使用C++. ...