我是刚刚接触多线程程序,10几页书弄了不短的时间,下面是自己的一点体会:
对于刚接触多线程编程的,调试多线程程序先要把线程和同步的定义搞明白,理解透了。多线程程序调试的时候是和非多线程程序不同的,两次调试的输出结果可能会不同,这是很正常的,就是运行两次输出也有可能不一样(不过几率不大)。还有就是我们的单步调试并不能精确的模拟程序非调试时的运行轨迹,有一部分是要我们自己去想象的。多线程的魅力也在于此。调试的时候要耐住性子,多写点输出句,慢慢分析,才能得到更多的东西。
进程与线程简介
进程(Process)是具有一定独立功能的程序关于某个数据集合上的一次运行活动,是系统进行资源分配和调度的一个独立单位。程序只是一组指令的有序集合,它本身没有任何运行的含义,只是一个静态实体。而进程则不同,它是程序在某个数据集上的执行,是一个动态实体。它因创建而产生,因调度而运行,因等待资源或事件而被处于等待状态,因完成任务而被撤消,反映了一个程序在一定的数据集上运行的全部动态过程。进程用来描述程序的执行过程而且可以作为共享资源的基本单位。
线程(Thread)是进程的一个实体,是CPU调度和分派的基本单位。线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。线程是进程的执行单元。当进程被初始化后,主线程就被创建了。对于绝大多数的应用程序来说,通常仅要求有一个主线程。尽管如此进程也可以创建额外的线程。线程是独立运行的,它且并不知道进程中还有其他线程存在。线程的执行是抢占式的,也就是说,当前运行的线程在任何时候都可能被挂起,以便另外一个线程可以运行。
线程和进程的关系是:线程是属于进程的,线程运行在进程空间内,同一进程所产生的线程共享同一内存空间,当进程退出时该进程所产生的线程都会被强制退出并清除。线程可与属于同一进程的其它线程共享进程所拥有的全部资源,但是其本身基本上不拥有系统资源,只拥有一点在运行中必不可少的信息(如程序计数器、一组寄存器和栈)。
实例代码:Mutex类
Mutex(互斥体)提供对一个进程的多个线程执行同步控制的能力,还用于对多个进程的不同线程实施同步控制。他的功能是阻止多个线程对一个共享资源并发访问。
using System.Runtime.CompilerServices;
using System.Threading;
using System.Collections.Generic;
using System.Text;
namespace MutexSample
... {
class Program
...{
static int sharedNumber = 42;
static Mutex localMut = new Mutex();
static bool isNew;
//out 表示通过引用来传递。这与 ref 类似,不同在于 ref 要求变量必须在传递之前初始化。若要使用 out 参数,方法定义和调用方法都必须显式使用 out
//参数1指示调用线程是否应拥有互斥体的初始所属权、参数2作为互斥体名,参数3方法返回时指示调用线程是否被授予互斥体的初始所属权
static Mutex globalMut = new Mutex(true, "Mutex Demo Global Mutex", out isNew);
static void Main(string[] args)
...{
if (!isNew)
...{
Console.WriteLine("This application is already running, shutting additional instance down.");
return;
}
// spin off a bunch of threads to perform //英语学习时间 spin off:创造新的事物而不影响原物的大小[稳定性]
// processing on a shared resource
Thread[] workers = new Thread[20];
for (int i = 0; i < 20; i++)
...{
Thread worker = new Thread(new ThreadStart(DoWork));
workers[i] = worker;
worker.Start();
}
foreach (Thread workerThread in workers)
workerThread.Join();
Console.WriteLine("All work finished, new value of shared resource is {0}", sharedNumber);
Console.ReadLine();
globalMut.ReleaseMutex();
}
//线程组中线程的共享代码
static void DoWork()
...{
// sit and wait until its OK to access
// the shared resource
//WaitOne()使互斥体等待一个(共享资源没被占用或释放的)信号(阻止当前进程),使访问共享资源而不必担心同步问题
localMut.WaitOne();
// modify shared resource
// multiple lines of code to modify
// to show consistent state of data
// within Mutex-protected block
Console.WriteLine("Accessing protected resource...");
sharedNumber += 2;
Console.WriteLine(sharedNumber);
sharedNumber -= 1;
Console.WriteLine(sharedNumber);
localMut.ReleaseMutex();
}
}
}
// 大尾巴狼注:当访问某共享资源的线程获得一个互斥体时,所以随后想访问改共享资源的互斥体必须等待第一个线程释放该共享资源。这项功能不是Mutex自动完成的要通过调用Mutex中的一些方法实现,例如:WaitOne()
实例代码:ReadWriteLock类
它允许一个线程中的代码读取共享时数据,单不需要创建一个同步代码块,也不会阻塞其他线程对该共享资源的读请求。ReadWriteLock只阻塞那些企图更新共享资源的请求,或者故意要在读取共享资源时对其加锁的请求。
using System.Threading;
using System.Collections.Generic;
using System.Text;
namespace ReadWriteLockDemo
{
class Program
{
// shared resource here is a simple int
static int i = 0 ;
static int sharedResource = 42 ;
static int numTimeouts = 0 ;
static ReaderWriterLock rwl = new ReaderWriterLock();
static void Main( string [] args)
{
// Create 10 threads that want write access
Thread[] writers = new Thread[ 10 ];
for ( int i = 0 ; i < 10 ; i ++ )
{
Thread writeThread = new Thread( new ThreadStart(DoWrite));
writers[i] = writeThread;
writers[i].Start();
}
Console.WriteLine( " 1111111111111111 " );
// Create 40 threads that want read access
Thread[] readers = new Thread[ 40 ];
for ( int j = 0 ; j < 40 ; j ++ )
{
Thread readThread = new Thread( new ThreadStart(DoRead));
readers[j] = readThread;
readers[j].Start();
}
// wait till they're all done
foreach (Thread writer in writers)
writer.Join();
foreach (Thread reader in readers)
reader.Join();
Console.WriteLine( " All work finished, only {0} timeouts. " , numTimeouts);
Console.ReadLine();
}
static void DoWrite()
{
try
{
rwl.AcquireWriterLock( 100 ); // 当超时就抛出异常,从而跳过部分代码
try
{
Interlocked.Increment( ref sharedResource); // 以原子操作的形式递增指定变量的值并存储结果,功能就是sharedResource++
Console.WriteLine( " Inspecting shared value {0} " , sharedResource);
Thread.Sleep( 15 );
}
finally
{
rwl.ReleaseWriterLock();
}
}
catch (ApplicationException ae)
{
Interlocked.Increment( ref numTimeouts);
}
}
static void DoRead()
{
try
{
rwl.AcquireReaderLock( 100 );
try
{
Console.WriteLine( " Inspecting shared value {0}...输出计数{1} " , sharedResource,i ++ );
}
finally
{
rwl.ReleaseReaderLock();
}
}
catch (ApplicationException ae)
{
Interlocked.Increment( ref numTimeouts);
}
}
}
}
// 大尾巴狼注:sharedResource+numTimeouts=52 这段代码线程比较多还有时间间隔参数,不要迷惑:1个主线程,分出2个线程组逐个的分解分析才能明白。调试看起来比较迷糊,黄点跳跃的太多.......