托管线程处理
- 格式:pdf
- 大小:289.51 KB
- 文档页数:9
托管和非托管的关系和区别(转帖)关键是要了解CLR(公共语言运行时).Net Framework 是由彼此独立又相关的两部分组成:CLR 和类库, CLR是它为我们提供的服务,类库是它实现的功能. .NET 的大部分特性----垃圾收集,版本控制,线程管理等,都使用了CLR提供的服务当你为.NET Framework编译源代码的时候,得到的目标代码不是CPU能识别的机器指令,而是一种叫做"微软中间语言(MSIL,或简称为IL的代码)"的新语言.CLR提供了一个实时编译器,用来把IL代码编译为本机机器代码.这样一来,CLR能够使代码变得可移植,因为.NET应用程序的源代码必须被编译为IL代码,这些IL代码可以运行在任何提供CLR服务的平台上.从CLR的角度来看,所有的语言都是平等的,只要有一个能生成IL代码的编译器就行,这就确保了各种语言的互操作性.为CLR而编写以及使用CLR服务的代码叫"托管代码",而那些未使用CLR服务的代码(也就是你多年以来一直编写的代码)叫"非托管代码".讨论到C++在.NET托管环境中的应用,由于C++不是为.NET平台设计的语言,所以,微软需要对C++作一些扩充,这就引入了"托管扩展"这一概念,它允许我们在C++项目中使用.NET Framework,如果有下列开发需要,就必须使用托管扩展:1. 将现有的C++代码移植到托管环境中2. 在C++代码中访问.NET Framework类3. 通过.NET语言访问现有的C++代码本文集中了多条常见的C#、.Net经典面试题目例如“.NET中类和结构的区别”、“页面之间传递值的几种方式?”,并简明扼要的给出了答案,希望能对学习C#、.Net的读者有所帮助。
1,请你说说.NET中类和结构的区别?答:结构和类具有大体的语法,但是结构受到的限制比类要多。
线程的三种实现方式线程是操作系统能够进行运算调度的最小单位,是进程中的一个实体,是被系统独立调度和执行的基本单位。
线程有三种实现方式,分别是用户级线程、内核级线程和轻量级进程。
下面将详细介绍这三种实现方式。
一、用户级线程(User-Level Threads,ULT)用户级线程是完全由用户程序实现和控制的线程。
用户级线程的创建、销毁和切换是通过用户程序的函数调用来完成的,与操作系统无关,不需要进行内核态和用户态之间的切换,由线程库在用户空间进行管理。
每当用户级线程调用了一个阻塞的系统调用,整个进程都会被阻塞住。
用户级线程的优点是实现上比较简单,可以根据具体应用的需要进行灵活的线程管理,而且切换线程的开销比较小。
缺点是由于用户级线程无法通过系统调用进行I/O操作,因此当一个线程阻塞时,整个进程都会被阻塞住,无法充分利用多核处理器的并行性能。
二、内核级线程(Kernel-Level Threads,KLT)内核级线程是由操作系统内核实现和管理的线程,调度、创建和销毁线程都在操作系统内核中完成,需要进行内核态和用户态之间的切换。
每个内核级线程都有自己的控制块,操作系统根据调度策略来调度线程的执行。
内核级线程的优点是能够充分利用多核处理器的并行性能,因为线程的调度都由操作系统内核完成。
缺点是创建和切换线程的开销比较大,会降低系统的整体性能。
三、轻量级进程(Lightweight Process,LWP)轻量级进程是一种中间形式的线程,在用户空间和内核空间的线程实现方式之间进行折中。
轻量级进程由用户程序创建和管理,但是它的创建、销毁和切换都是由操作系统内核来完成的,使用内核级线程实现线程的调度。
轻量级进程的优点是能够充分利用多核处理器的并行性能,同时由于线程的创建和切换都由操作系统内核完成,因此能够更好地支持I/O操作,不会出现用户级线程阻塞导致整个进程阻塞的情况。
缺点是由于需要进行内核态和用户态之间的切换,创建和切换线程的开销比用户级线程大,但是相比于内核级线程来说要小得多。
python多线程的高级用法,以及线程同步和互斥机制Python 的多线程模块 `threading` 提供了一种方法来创建和管理线程。
下面是一些 Python 多线程的高级用法,以及线程同步和互斥机制的介绍。
高级用法1. 线程局部存储:使用 `()` 可以为每个线程提供独立的存储空间。
这对于在线程之间存储和检索数据非常有用。
```pythonimport threading创建一个线程局部存储对象thread_local = ()def worker():设置线程局部变量的值thread_ = "thread-{}".format(_thread().name)print(thread_)threads = []for i in range(5):t = (target=worker)(t)()```2. 线程池:使用 `` 可以更方便地管理线程池。
这个类提供了一个 `map()` 方法,可以并行地对可迭代对象中的每个元素执行函数。
```pythonfrom import ThreadPoolExecutordef square(n):return n nwith ThreadPoolExecutor(max_workers=5) as executor:results = (square, range(10))```3. 线程锁:使用 `` 可以实现线程之间的互斥。
当一个线程拥有锁时,其他线程必须等待锁被释放后才能继续执行。
```pythonlock = ()with lock:临界区,只有一个线程可以执行这部分代码pass```4. 信号量:使用 `` 可以实现线程之间的同步。
信号量是一个计数器,用于控制同时访问共享资源的线程数量。
```pythonfrom import Semaphoresem = Semaphore(3) 最多允许3个线程同时访问共享资源with sem:临界区,只有当信号量计数大于0时,线程才能执行这部分代码pass```5. 事件循环:使用 `asyncio` 模块可以实现异步 I/O 和协程的并发执行。
第4章线程管理与控制4.1 线程概念简介每个进程都拥有自己的数据段、代码段和堆栈段,这就造成了进程在进行切换等操作时都需要有比较复杂的上下文切换等动作。
为了进一步减少处理机的空转时间,支持多处理器以及减少上下文切换开销,进程在演化中出现了另一个概念——线程。
它是进程独立的一条运行路线,处理器调度的最小单元,也可以称为轻量级进程。
线程可以对进程的存空间和资源进行访问,并与同一进程中的其他线程共享。
因此,线程的上下文切换的开销比创建进程小很多。
同进程一样,线程也将相关的执行状态和存储变量放在线程控制块(TCB)。
一个进程可以有多个线程,也就是有多个线程控制块及堆栈寄存器,但却共享一个用户地址空间。
要注意的是,由于线程共享了进程的资源和地址空间,因此,任何线程对系统资源的操作都会给其他线程带来影响。
由此可知,多线程中的同步是非常重要的问题。
在多线程系统中,进程与进程的关系如图所示。
进程与线程关系4.2 Linu*多线程编程API与实验任务4.3.1 Linu*多线程编程API创建线程pthread_create()函数实际上就是确定调用该线程函数的入口点,在线程创建以后,就开始运行相关的线程函数,在该函数运行完之后,该线程也就退出了,这也是线程退出一种方法。
另一种退出线程的方法是使用函数pthread_e*it(),这是线程的主动行为。
这里要注意的是,在使用线程函数时,不能随意使用e*it()退出函数进行出错处理,由于e*it()的作用是使调用进程终止,往往一个进程包含多个线程,因此,在使用e*it()之后,该进程中的所有线程都终止了。
因此,在线程中就可以使用pthread_e*it()来代替进程中的e*it()。
由于一个进程中的多个线程是共享数据段的,因此通常在线程退出之后,退出线程所占用的资源并不会随着线程的终止而得到释放。
正如进程之间可以用wait()系统调用来同步终止并释放资源一样,线程之间也有类似机制,那就是pthread_join()函数。
Argobots 基本原理1. 引言Argobots是一个基于用户级线程(User-level Threads, ULTs)的并行计算框架,用于提供高效的并行计算和异步任务调度。
它的设计目标是为了充分利用现代计算机体系结构中的多核和多处理器资源,提供高度灵活和可扩展的并行计算能力。
Argobots的基本原理涉及线程管理、调度器、工作单元和同步机制等方面。
下面将详细介绍每个方面的基本原理。
2. 线程管理Argobots使用用户级线程(ULTs)来实现并行计算。
每个ULT都是一个独立的执行单元,可以独立地执行计算任务。
Argobots提供了一套线程管理机制,用于创建、销毁和管理ULTs。
Argobots使用线程池来管理ULTs。
线程池中维护了一组预创建的ULTs,当有新的计算任务到来时,线程池中的ULTs会被动态分配给任务进行执行。
这种线程池的机制可以减少线程创建和销毁的开销,提高系统的性能和资源利用率。
3. 调度器Argobots的调度器负责将任务分配给可用的ULTs进行执行。
调度器采用了工作窃取(Work Stealing)的策略,以实现负载均衡和任务并行化。
调度器维护了一个任务队列,其中包含待执行的任务。
当一个ULT完成了当前任务后,它会从任务队列中获取新的任务进行执行。
如果任务队列为空,ULT可以从其他ULT的任务队列中窃取任务进行执行。
这种工作窃取的策略可以使得任务在多个ULT之间动态平衡,提高系统的并行性能。
4. 工作单元Argobots将计算任务封装为工作单元(Work Unit)。
工作单元是最小的执行单位,可以是一个函数、一个代码块或者一个任务。
每个工作单元都会被分配给一个ULT进行执行。
Argobots提供了一套工作单元管理机制,用于创建、销毁和管理工作单元。
工作单元可以被动态地创建和销毁,以满足不同计算任务的需求。
同时,工作单元也可以被分配给不同的ULTs进行执行,以实现任务的并行执行。
一、概述线程是多任务处理中的一个重要概念,而线程间通信则是在多个线程处理不同任务的情况下,需要进行数据共享和交流的重要问题。
在Java语言中,线程间通信的方式有多种,本文将对几种常用的线程间通信方法进行介绍和分析。
二、共享内存1. 共享内存是一种通过在多个线程之间共享变量来进行通信的方式。
在Java中,可以使用共享变量来实现线程间通信,例如使用volatile关键字进行变量的共享。
2. 共享内存的优点是实现简单,但在多线程并发操作时会导致数据不一致问题,需要谨慎处理同步和顺序性的问题。
三、管程(Monitor)和synchronized关键字1. 管程是一种通过对象的加锁和解锁来进行线程间通信的方式。
在Java中,可以使用synchronized关键字对共享对象进行加锁和解锁,实现线程间的同步和互斥操作。
2. 管程的优点是可以有效解决共享变量操作的同步和顺序性问题,但在使用synchronized关键字时需要注意避免死锁和性能问题的发生。
四、w本人t()、notify()和notifyAll()方法1. w本人t()、notify()和notifyAll()是Object类中定义的几种用于线程间通信的方法。
2. w本人t()方法可以让线程等待,并释放对象的锁;notify()方法可以唤醒一个等待的线程;notifyAll()方法可以唤醒所有等待的线程。
3. 使用w本人t()、notify()和notifyAll()方法可以实现线程间的协作和通信,但需要注意避免虚假唤醒和线程安全问题。
五、并发队列(ConcurrentQueue)1. 并发队列是一种通过队列数据结构来实现线程安全的共享对象,通常用于生产者-用户模式的线程间通信。
2. Java中提供了ConcurrentLinkedQueue和BlockingQueue等并发队列实现,可以实现多线程间的数据交换和共享,避免了手动同步和加锁的操作。
六、信号量(Semaphore)和倒计数器(CountDownLatch)1. 信号量和倒计数器是两种用于控制并发线程执行顺序和数量的同步工具。
使用线程和线程处理本节内容启动时创建线程并传递数据讨论并演示如何创建托管线程,包括如何将数据传递到新线程以及如何返回数据。
暂停和继续线程讨论暂停和继续执行托管线程的后果。
销毁线程讨论销毁托管线程的后果,以及如何处理ThreadAbortException。
调度线程讨论线程优先级及它们如何影响线程调度。
一、启动时创建线程并传递数据在创建操作系统进程时,操作系统将插入一个线程以执行该进程(包括任何原始应用程序域)中的代码。
从此刻起,就可以创建和销毁应用程序域,而不必创建或销毁任何操作系统线程。
如果正在执行的代码是托管代码,则可以通过检索类型为Thread的静态CurrentThread属性来获取正在当前应用程序域中执行的线程的Thread对象。
本主题描述线程创建,并讨论用于向线程过程传递数据的替代方法。
创建线程创建新的Thread对象时,将创建新的托管线程。
Thread类具有接受一个ThreadStart委托或ParameterizedThreadStart委托的构造函数:该委托包装调用Start方法时由新线程调用的方法。
多次调用Start将引发ThreadStateException。
Start方法立即返回,经常是在实际启动新线程之前。
可以使用ThreadState和IsAlive属性确定任何时刻的线程状态,但是绝不应该将这些属性用于同步线程活动。
注意下面的代码示例创建两个新线程以调用另一个对象上的实例和静态方法。
C#using System;using System.Threading;public class ServerClass{// The method that will be called when the thread is starte d.public void InstanceMethod(){Console.WriteLine("ServerClass.InstanceMethod is running on another th read.");// Pause for a moment to provide a delay to make// threads more apparent.Thread.Sleep(3000);Console.WriteLine("The instance method called by the worker thread has ended.");}public static void StaticMethod(){Console.WriteLine("ServerClass.StaticMethod is running on another thre ad.");// Pause for a moment to provide a delay to make// threads more apparent.Thread.Sleep(5000);Console.WriteLine("The static method called by the worker thread has e nded.");}}public class Simple{public static int Main(String[] args){Console.WriteLine("Thread Simple Sample");ServerClass serverObject = new ServerClass();// Create the thread object, passing in the// serverObject.InstanceMethod method using a// ThreadStart delegate.Thread InstanceCaller = new Thread(new ThreadStart(serverObject.InstanceMethod));// Start the thread.InstanceCaller.Start();Console.WriteLine("The Main() thread calls this after " + "starting the new InstanceCaller thread.");// Create the thread object, passing in the// serverObject.StaticMethod method using a// ThreadStart delegate.Thread StaticCaller = new Thread(new ThreadStart(ServerClass.StaticMethod));// Start the thread.StaticCaller.Start();Console.WriteLine("The Main() thread calls this after " + "starting the new StaticCaller thread.");return 0;}}将数据传递给线程和从线程检索数据在.NET Framework 2.0 版中,ParameterizedThreadStart委托提供了一种简便方法,可以在调用System.Threading.Thread.Start(System.Object)方法重载时将包含数据的对象传递给线程。
qt多线程movetothread使用方式Qt中的多线程编程可以使用`QThread`和`QThreadPool`两种方式来实现。
1. 使用`QThread`:- 创建一个继承自`QThread`的自定义类,并重写其`run()`函数,实现子线程的逻辑。
- 在主线程中通过创建自定义类的对象,并调用`start()`函数,启动子线程。
- 在主线程中可以通过`wait()`函数等待子线程的结束。
示例代码:```cpp#include <QThread>class WorkerThread : public QThread{Q_OBJECTpublic:void run() override{// 在子线程中执行的逻辑}};int main(int argc, char *argv[]){QApplication a(argc, argv);WorkerThread workerThread;workerThread.start(); // 启动子线程workerThread.wait(); // 等待子线程结束return a.exec();}```2. 使用`movetoThread`:- 创建一个继承自`QObject`的自定义类,并在其中实现需要在子线程执行的逻辑。
- 在主线程中创建一个子线程对象,然后将自定义类的对象移动到子线程中。
- 最后,启动子线程。
示例代码:```cpp#include <QObject>#include <QThread>class WorkerObject : public QObject{Q_OBJECTpublic slots:void doWork(){// 在子线程中执行的逻辑}};int main(int argc, char *argv[]){QApplication a(argc, argv);QThread workerThread;WorkerObject workerObject;workerObject.moveToThread(&workerThread); // 将对象移动到子线程中// 连接信号和槽QObject::connect(&workerThread, &QThread::started,&workerObject, &WorkerObject::doWork);workerThread.start(); // 启动子线程return a.exec();}```注意事项:- 需要在主线程中创建和启动子线程。
线程与并发控制:处理多线程的同步和互斥线程和并发控制是计算机科学领域中非常重要的概念,特别是在多核处理器和分布式系统中。
线程是程序执行的基本单位,而并发控制则是指有效地管理多个线程之间的同步和互斥,以保证数据的一致性和程序的正确执行。
在多线程编程中,线程之间的并发控制是一个关键问题。
当多个线程同时访问共享资源时,如果没有适当的同步和互斥机制,就会出现数据竞争和不一致的问题。
因此,了解如何处理线程的同步和互斥是非常重要的。
同步指的是多个线程之间按照一定的顺序执行,以保证数据的一致性。
常见的同步机制包括互斥锁、条件变量、信号量等。
互斥锁是最基本的同步机制,它可以确保同时只有一个线程能访问共享资源,从而避免数据竞争。
条件变量可以在多个线程之间传递信号,以协调它们的执行流程。
信号量可以用来控制并发访问资源的数量,避免资源的过度竞争。
除了同步机制外,还有一些高级的并发控制技术,如读写锁、原子操作、事务内存等。
读写锁可以提高多线程读取共享资源的效率,因为读取操作不涉及数据一致性问题,可以同时进行。
原子操作可以确保某些操作的原子性,即要么全部执行成功,要么全部不执行。
事务内存是一种基于硬件的并发控制技术,可以提供更高的性能和可靠性。
在处理多线程的同步和互斥时,需要遵循一些基本原则。
首先,避免死锁,即当多个线程互相等待对方释放资源时,就会陷入死锁状态。
其次,避免资源泄漏,即确保每个线程在完成任务后释放所有的资源。
最后,避免竞争条件,即多个线程对共享资源的访问顺序可能影响程序的正确执行,需要避免这种情况的发生。
为了提高多线程程序的性能和可靠性,在设计和实现上需要注意一些细节。
首先,尽量减少共享资源的数量,因为共享资源越多,就越容易引发数据竞争和并发控制问题。
其次,合理设计线程的通信和同步机制,避免不必要的等待和阻塞。
最后,尽量避免线程间频繁地切换和竞争,提高程序的并发执行效率。
总的来说,线程和并发控制是计算机科学中非常重要的概念,能够有效地提高程序的性能和可靠性。
C# 线程池基础池(Pool)是一个很常见的提高性能的方式。
比如线程池连接池等,之所以有这些池是因为线程和数据库连接的创建和关闭是一种比较昂贵的行为。
对于这种昂贵的资源我们往往会考虑在一个池容器中放置一些资源,在用的时候去拿,在不够的时候添点,在用完就归还,这样就可以避免不断的创建资源和销毁资源。
如果您做过相关实验的话可能会觉得不以为然,似乎开1000个线程也用不了几百毫秒。
我们要这么想,对于一个高并发的环境来说,每一秒假设有100 个请求,每个请求需要使用(开和关)10个线程,也就是一秒需要处理1000个线程的开和关,每个线程独立堆栈1M,可以想象在这一秒中内存分配和回收是多么夸张,这个开销不能说不昂贵。
首先,要理解线程池线程分为两类工作线程和IO线程,可以单独设置最小线程数和最大线程数:ThreadPool.SetMinThreads(2, 2);ThreadPool.SetMaxThreads(4, 4);最大线程数很好理解,就是线程池最多创建这些线程,如果最大4个线程,现在这4个线程都在运行的话,后续进来的线程只能排队等待了。
那么为什么有最小线程一说法呢?其实之所以使用线程池是不希望线程在创建后运行结束后理解回收,这样的话以后要用的时候还需要创建,我们可以让线程池至少保留几个线程,即使没有线程在工作也保留。
上述语句我们设置线程池一开始就保持2个工作线程和2个IO线程,最大不超过4个线程。
至于线程池的使用相当简单先来看一段代码:for (int i = 0; i < totalThreads; i++){ThreadPool.QueueUserWorkItem(o =>{Thread.Sleep(1000);int a, b;ThreadPool.GetAvailableThreads(out a, out b);Console.WriteLine(string.Format("({0}/{1}) #{2} : {3}", a, b, Thread.CurrentThread.ManagedThreadId,DateTime.Now.ToString("mm:ss")));});}Console.WriteLine("Main thread finished");Console.ReadLine();代码里面用到了一个事先定义的静态字段:static readonly int totalThreads = 10;代码运行结果如下:每一个线程都休眠一秒然后输出当前线程池可用的工作线程和IO线程以及当前线程的托管ID和时间。
托管线程处理托管线程处理基本知托管线程处理基本知识识线程与线程处线程与线程处理理操作系统使用进程将它们正在执行的不同应用程序分开。
线程是操作系统分配处理器时间的基本单元,并且进程中可以有多个线程同时执行代码。
每个线程都维护异常处理程序、调度优先级和一组系统用于在调度该线程前保存线程上下文的结构。
线程上下文包括为使线程在线程的宿主进程地址空间中无缝地继续执行所需的所有信息,包括线程的 CPU 寄存器组和堆栈。
.NET Framework 将操作系统进程进一步细分为由 System.AppDomain表示的、称为应用程序域的轻量托管子进程。
一个或多个托管线程(由 System.Threading.Thread 表示)可以在同一个托管进程中的一个或任意数目的应用程序域中运行。
虽然每个应用程序域都是用单个线程启动的,但该应用程序域中的代码可以创建附加应用程序域和附加线程。
其结果是托管线程可以在同一个非托管进程中的应用程序域之间自由移动;您可能只有一个线程在若干应用程序域之间移动。
支持抢先多任务处理的操作系统可以创建多个进程中的多个线程同时执行的效果。
它通过以下方式实现这一点:在需要处理器时间的线程之间分割可用处理器时间,并轮流为每个线程分配处理器时间片。
当前执行的线程在其时间片结束时被挂起,而另一个线程继续运行。
当系统从一个线程切换到另一个线程时,它将保存被抢先的线程的线程上下文,并重新加载线程队列中下一个线程的已保存线程上下文。
时间片的长度取决于操作系统和处理器。
由于每个时间片都很小,因此即使只有一个处理器,多个线程看起来似乎也是在同时执行。
这实际上就是多处理器系统中发生的情形,在此类系统中,可执行线程分布在多个可用处理器中。
多个线程的优多个线程的优点点无论如何,要提高对用户的响应速度并且处理所需数据以便几乎同时完成工作,使用多个线程是一种最为强大的技术。
在具有一个处理器的计算机上,多个线程可以通过利用用户事件之间很小的时间段在后台处理数据来达到这种效果。
例如,在另一个线程正在重新计算同一应用程序中的电子表格的其他部分时,用户可以编辑该电子表格。
无需修改,同一个应用程序在具有多个处理器的计算机上运行时将极大地满足用户的需要。
单个应用程序域可以使用多个线程来完成以下任务:•通过网络与 Web 服务器和数据库进行通信。
•执行占用大量时间的操作。
•区分具有不同优先级的任务。
例如,高优先级线程管理时间关键的任务,低优先级线程执行其他任务。
• 使用户界面可以在将时间分配给后台任务时仍能快速做出响应。
多个线程的缺多个线程的缺点点建议您使用尽可能少的线程,这样可以最大限度地减少操作系统资源的使用,并可提高性能。
线程处理还具有在设计应用程序时要考虑的资源要求和潜在冲突。
这些资源要求如下所述:•系统将为进程、AppDomain 对象和线程所需的上下文信息使用内存。
因此,可以创建的进程、AppDomain 对象和线程的数目会受到可用内存的限制。
•跟踪大量的线程将占用大量的处理器时间。
如果线程过多,则其中大多数线程都不会产生明显的进度。
如果大多数当前线程处于一个进程中,则其他进程中的线程的调度频率就会很低。
•使用许多线程控制代码执行非常复杂,并可能产生许多 bug 。
• 销毁线程需要了解可能发生的问题并对那些问题进行处理。
提供对资源的共享访问会造成冲突。
为了避免冲突,必须对共享资源进行同步或控制对共享资源的访问。
如果在相同或不同的应用程序域中未能正确地使访问同步,则会导致出现一些问题,这些问题包括死锁和争用条件等,其中死锁是指两个线程都停止响应,并且都在等待对方完成;争用条件是指由于意外地出现对两个事件的执行时间的临界依赖性而发生反常的结果。
系统提供了可用于协调多个线程之间的资源共享的同步对象。
减少线程的数目使同步资源更为容易。
需要同步的资源包括:•系统资源(如通信端口)。
•多个进程所共享的资源(如文件句柄)。
• 由多个线程访问的单个应用程序域的资源(如全局、静态和实例字段)。
线程处理与应用程序设线程处理与应用程序设计计一般情况下,要为不会阻止其他线程的相对较短的任务处理多个线程并且不需要对这些任务执行任何特定调度时,使用 ThreadPool 类是一种最简单的方式。
但是,有多个理由创建您自己的线程:•如果您需要使一个任务具有特定的优先级。
•如果您具有可能会长时间运行(并因此阻止其他任务)的任务。
•如果您需要将线程放置到单线程单元中(所有 ThreadPool 线程均处于多线程单元中)。
•如果您需要与该线程关联的稳定标识。
例如,您应使用一个专用线程来中止该线程,将其挂起或按名称发现它。
• 如果您需要运行与用户界面交互的后台线程,.NET Framework 2.0 版提供了 BackgroundWorker组件,该组件可以使用事件与用户界面线程的跨线程封送进行通信。
为多线程处理同步数为多线程处理同步数据据当多个线程可以调用单个对象的属性和方法时,对这些调用进行同步处理是非常重要的。
否则,一个线程可能会中断另一个线程正在执行的任务,使该对象处于一种无效状态。
其成员不受这类中断影响的类叫做线程安全类。
“公共语言基础结构”提供了几种可用来同步对实例和静态成员的访问的策略:•同步代码区域。
可以使用 Monitor 类或此类的编译器支持来仅同步需要此类的代码块,从而提高性能。
•手动同步。
可以使用 .NET Framework 类库提供的同步对象。
请参见 同步基元概述,这部分对 Monitor 类进行了讨论。
•同步上下文。
可以使用 SynchronizationAttribute 为 ContextBoundObject 对象启用简单的自动同步。
• Synchronized 属性。
Hashtable 和 Queue 等几个类提供了一个可为该类的实例返回线程安全包装的Synchronized 属性。
请参见集合和同步(线程安全)。
前台和后台线前台和后台线程程托管线程或者是后台线程,或者是前台线程。
后台线程不会使托管执行环境处于运行状态,除此之外,后台线程与前台线程是一样的。
一旦所有前台线程在托管进程(其中 .exe文件是托管程序集)中被停止,系统将停止所有后台线程并关闭。
注意注意:: 当运行库因为进程关闭而停止某个后台线程时,不会在该线程中引发异常。
但是,当线程是因为 AppDomain.Unload 方法卸载应用程序域而停止时,将同时在后台和前台线程中引发 ThreadAbortException 。
可使用 Thread.IsBackground 属性确定线程是后台线程还是前台线程,或更改其状态。
通过将其 IsBackground 属性设置为 true ,可在任何时候将线程更改为后台线程。
重要说明重要说明::线程的前台或后台状态不影响线程中未处理异常的结果。
在 .NET Framework 2.0版中,前台或后台线程中的未处理异常都将导致应用程序终止。
请参见 托管线程中的异常。
属于托管线程池的线程(即其 IsThreadPoolThread 属性为 true的线程)是后台线程。
从非托管代码进入托管执行环境的所有线程都被标记为后台线程。
通过创建并启动新的 Thread 对象而生成的所有线程都默认为前台线程。
如果使用一个线程监视活动(例如套接字连接),请将其 IsBackground 属性设置为true ,以便该线程不会阻止进程终止。
托管线程处理的最佳做托管线程处理的最佳做法法使用多线程时要考虑以下准则:•不要使用 Thread.Abort 终止其他线程。
对另一个线程调用 Abort 无异于引发该线程的异常,也不知道该线程已处理到哪个位置。
•不要使用 Thread.Suspend 和 Thread.Resume 同步多个线程的活动。
请使用 Mutex 、ManualResetEvent 、AutoResetEvent 和 Monitor 。
•不要从主程序中控制辅助线程的执行(如使用事件),而应在设计程序时让辅助线程负责等待任务,执行任务,并在完成时通知程序的其他部分。
如果辅助线程不阻止,请考虑使用线程池线程。
Monitor.PulseAll 在辅助线程阻止的情况下很有用。
• 不要将类型用作锁定对象。
例如,避免在 C# 中使用 lock(typeof(X)) 代码,或在 Visual Basic中使用 SyncLock(GetType(X)) 代码,或将 Monitor.Enter 和 Type对象一起使用。
对于给定类型,每个应用程序域只有一个 System.Type 实例。
如果您锁定的对象的类型是 public ,您的代码之外的代码也可锁定它,但会导致死锁。
有关其他信息,请参见可靠性最佳做法。
• 锁定实例时要谨慎,例如,C# 中的 lock(this) 或 Visual Basic 中的SyncLock(Me)。
如果您的应用程序中不属于该类型的其他代码锁定了该对象,则会发生死锁。
• 一定要确保已进入监视器的线程始终离开该监视器,即使当线程在监视器中时发生异常也是如此。
C# 的 lock 语句和 Visual Basic 的 SyncLock 语句可自动提供此行为,它们用一个 finally 块来确保调用Monitor.Exit 。
如果无法确保调用 Exit ,请考虑将您的设计更改为使用 Mutex 。
Mutex在当前拥有它的线程终止后会自动释放。
• 一定要针对那些需要不同资源的任务使用多线程,避免向单个资源指定多个线程。
例如,任何涉及 I/O的任务都会从其拥有其自己的线程这一点得到好处,因为此线程在 I/O操作期间将阻止,从而允许其他线程执行。
用户输入是另一种可从专用线程获益的资源。
在单处理器计算机上,涉及大量计算的任务可与用户输入和涉及 I/O 的任务并存,但多个计算量大的任务将相互竞争。
• 对于简单的状态更改,请考虑使用 Interlocked 类的方法,而不是 lock 语句(在 Visual Basic 中为SyncLock )。
lock 语句是一个优秀的通用工具,但是 Interlocked类为必须是原子性的更新提供了更好的性能。
如果没有争夺,它会在内部执行一个锁定前缀。
在查看代码时,请注意类似于以下示例所示的代码。
在第一个示例中,状态变量是递增的: Visual Basic 复制代码 SyncLock lockObject myField += 1 End SyncLock C# 复制代码 lock(lockObject) { myField++; } • 可以使用 Increment 方法代替 lock 语句,从而提高性能,如下所示: Visual Basic 复制代码 System.Threading.Interlocked.Increment(myField)C# 复制代码System.Threading.Interlocked.Increment(myField);注意注意::在 .NET Framework 2.0 版中,Add 方法提供增量大于 1 的原子更新。