在.net面向对象程序设计阶段在线程资源共享中的线程安全和线程冲突的解决方案;多线程同步,使用线程锁和线程通知实现线程同步,具体内容介绍如下:
1、 ThreadStatic特性
特性:[ThreadStatic]
功能:指定静态字段在不同线程中拥有不同的值
在此之前,我们先看一个多线程的示例:
我们定义一个静态字段:
static int num = 0;
然后创建两个线程进行分别累加:
new Thread(() => { for (int i = 0; i < 1000000; i++) ++num; Console.WriteLine("来自{0}:{1}", Thread.CurrentThread.Name, num); }) { Name = "线程一" }.Start(); new Thread(() => { for (int i = 0; i < 2000000; i++) ++num; Console.WriteLine("来自{0}:{1}", Thread.CurrentThread.Name, num); }) { Name = "线程二" }.Start();
运行多次结果如下:
可以看到,三次的运行结果均不相同,产生这种问题的原因是多线程中同步共享问题导致的,即是多个线程同时共享了一个资源。如何解决上述问题,最简单的方法就是使用静态字段的ThreadStatic特性。
在定义静态字段时,加上[ThreadStatic]特性,如下:
[ThreadStatic] static int num = 0;两个线程不变的情况下,再次运行,结果如下:
不论运行多少次,结果都是一样的,当字段被ThreadStatic特性修饰后,它的值在每个线程中都是不同的,即每个线程对static字段都会重新分配内存空间,就当然于一次new操作,这样一来,由于static字段所产生的问题也就没有了。
2. 资源共享
多线程的资源共享,也就是多线程同步(即资源同步),需要注意的是线程同步指的是线程所访问的资源同步,并非是线程本身的同步。
在实际使用多线程的过程中,并非都是各个线程访问不同的资源。
下面看一个线程示例,假如我们并不知道线程要多久完成,我们等待一个固定的时间(假如是500毫秒):
先定义一个静态字段:
static int result;
创建线程:
Thread myThread = new Thread(() => { Thread.Sleep(1000); result = 100; }); myThread.Start(); Thread.Sleep(500); Console.WriteLine(result);
运行结果如下:
可以看到结果是0,显然不是我们想要的,但往往在线程执行过程中,我们并不知道它要多久完成,能不能在线程完成后有一个通知?
这里有很多笨的方法,比如我们可能会想到使用一个循环来检测线程状态,这些都不是理想的。
.NET为我们提供了一个Join方法,就是线程阻塞,可以解决上述问题,我们使用Stopwatch来记时,
改进线程代码如下:
System.Diagnostics.Stopwatch watch = System.Diagnostics.Stopwatch.StartNew(); Thread myThread = new Thread(() => { Thread.Sleep(1000); result = 100; }); myThread.Start(); Thread.Sleep(500); myThread.Join(); Console.WriteLine(watch.ElapsedMilliseconds); Console.WriteLine(result);
运行结果如下:
结果和我们想要的是一致的。
3. 线程锁
除了上面示例的方法,对于线程同步,.NET还为我们提供了一个锁机制来解决同步,再次改进上面示例如下:
先定义一个静态字段来存储锁:
static object locker = new html" target="_blank">object();
这里我们可以先不用考虑这个对象是什么。继续看改进后的线程:
System.Diagnostics.Stopwatch watch = System.Diagnostics.Stopwatch.StartNew(); Thread t1 = new Thread(() => { lock (locker) { Thread.Sleep(1000); result = 100; } }); t1.Start(); Thread.Sleep(100); lock (locker) { Console.WriteLine("线程耗时:"+watch.ElapsedMilliseconds); Console.WriteLine("线程输出:"+result); }
运行结果如下:
运行结果和上面示例一样,如果线程处理过程较复杂,可以看到耗时明显减少,这是一种用比阻塞更效率的方式完成线程同步。
4. 线程通知
前面说到了能否在一个线程完成后,通知等待的线程呢,这里.NET为我们提供了一个事件通知的方法来解决这个问题。
4.1 AutoResetEvent
先定义一个通知对象
static EventWaitHandle tellMe = new AutoResetEvent(false);
System.Diagnostics.Stopwatch watch = System.Diagnostics.Stopwatch.StartNew(); Thread myThread = new Thread(() => { Thread.Sleep(1000); result = 100; tellMe.Set(); }); myThread.Start(); tellMe.WaitOne(); Console.WriteLine("线程耗时:" + watch.ElapsedMilliseconds); Console.WriteLine("线程输出:" + result);
运行结果如下:
4.2 ManualResetEvent
和AutoResetEvent 相对的还有一个 ManualResetEvent 手动模式,他们的区别在于,在线程结束后ManualResetEvent 还是可以通行的,除非手动Reset关闭。下面看一个示例:
先定义一个手动通知的对象:
static EventWaitHandle mre = new ManualResetEvent(false);
创建线程:
System.Diagnostics.Stopwatch watch = System.Diagnostics.Stopwatch.StartNew(); Thread myThreadFirst = new Thread(() => { Thread.Sleep(1000); result = 100; mre.Set(); }) { Name = "线程一" }; Thread myThreadSecond = new Thread(() => { mre.WaitOne(); Console.WriteLine(Thread.CurrentThread.Name + "获取结果:" + result + "("+System.DateTime.Now.ToString()+")"); }) { Name="线程二"}; myThreadFirst.Start(); myThreadSecond.Start(); mre.WaitOne(); Console.WriteLine("线程耗时:" + watch.ElapsedMilliseconds + "(" + System.DateTime.Now.ToString() + ")"); Console.WriteLine("线程输出:" + result + "(" + System.DateTime.Now.ToString() + ")");
运行结果如下:
4.3. Semaphore
Semaphore也是线程通知的一种,上面的通知模式,在线程开启的数量很多的情况下,使用Reset()关闭时,如果不使用Sleep休眠一下,很有可能导致某些线程没有恢复的情况下,某一线程提前关闭,对于这种很难预测的情况,.NET提供了更高级的通知方式Semaphore,可以保证在超多线程时不会出现上述问题。
先定义一个通知对象的静态字段:
static Semaphore sem = new Semaphore(2, 2);
for (int i = 1; i <= 100; i++) { new Thread(() => { sem.WaitOne(); Thread.Sleep(30); Console.WriteLine(Thread.CurrentThread.Name+" "+DateTime.Now.ToString()); sem.Release(); }) { Name="线程"+i}.Start(); }
运行结果如下:
可以看到完整的输出我们所想要看到的结果。
5. 本节要点:
A.线程中静态字段的ThreadStatic特性,使用该字段在不同线程中拥有不同的值
B.线程同步的几种方式,线程锁和线程通知
C.线程通知的两种方式:AutoResetEvent /ManualResetEvent 和 Semaphore
到此为止.net面向对象之多线程(Multithreading)及多线程高级应用介绍到此为止。
thread被定义为程序的执行路径。 每个线程定义一个独特的控制流。 如果您的应用程序涉及复杂且耗时的操作,那么设置不同的执行路径或线程通常很有帮助,每个线程执行特定的工作。 线程是lightweight processes 。 使用线程的一个常见示例是现代操作系统的并发编程的实现。 线程的使用可以节省CPU周期的浪费并提高应用程序的效率。 到目前为止,我们编写了程序,其中单个线程作为单个进程运行
Java是一种multi-threaded programming language ,这意味着我们可以使用Java开发多线程程序。 多线程程序包含两个或多个可以并发运行的部分,每个部分可以同时处理不同的任务,特别是在计算机有多个CPU时,可以充分利用可用资源。 根据定义,多任务处理是指多个进程共享公共处理资源(如CPU)。 多线程将多任务处理的概念扩展到可以将单个应用程序中的特定操作细分为单个线
线程的定义是程序的执行路径。每个线程都定义了一个独特的控制流,如果应用程序涉及到复杂且耗时的操作,那幺设置不同的线程执行路径会非常有好处,因为每个线程会被指定于执行特定的工作。 线程实际上是轻量级进程。一个常见的使用线程的实例是现代操作系统中的并行编程。使用线程不仅有效地减少了 CPU 周期的浪费,同时还提高了应用程序的运行效率。 到目前为止我们所编写的程序都是以一个单线程作为应用程序的运行的,其
本文向大家介绍15个高级Java多线程面试题及回答,包括了15个高级Java多线程面试题及回答的使用技巧和注意事项,需要的朋友参考一下 Java 线程面试问题 在任何Java面试当中多线程和并发方面的问题都是必不可少的一部分。如果你想获得任何股票投资银行的前台资讯职位,那么你应该准备很多关于多线程的问题。在投资银行业务中多线程和并发是一个非常受欢迎的话题,特别是电子交易发展方面相关的。他们会问面试
数据封装、继承和多态只是面向对象程序设计中最基础的3个概念。在Python中,面向对象还有很多高级特性,允许我们写出非常强大的功能。 我们会讨论多重继承、定制类、元类等概念。