8. 执行线程
8.1. 简介
当应用程序启动时,它会在称为线程的执行流中运行。在 .NET 中,表示线程的类是 System.Threading.Thread 类,其定义如下:

我们将仅使用该类中的部分属性和方法:
返回当前正在运行的线程 | |
线程名称 | |
表示线程是否处于活动状态(true)或非活动状态(false) | |
启动线程的执行 | |
永久停止线程的执行 | |
使线程暂停 n 毫秒 | |
暂时暂停线程的执行 | |
恢复已暂停线程的执行 | |
阻塞操作 - 等待线程完成后才继续执行下一条指令 |
让我们看一个简单的应用程序,它演示了主执行线程的存在,即类中的 Main 函数运行的那个线程:
' use of threads
Imports System
Imports System.Threading
Public Module thread1
Public Sub Main()
' init current thread
Dim main As Thread = Thread.CurrentThread
' display
Console.Out.WriteLine(("Thread courant : " + main.Name))
' we change the name
main.Name = "main"
' check
Console.Out.WriteLine(("Thread courant : " + main.Name))
' infinite loop
While True
' display
Console.Out.WriteLine((main.Name + " : " + DateTime.Now.ToString("hh:mm:ss")))
' temporary shutdown
Thread.Sleep(1000)
End While
End Sub
End Module
屏幕显示结果:
dos>thread1
Thread courant :
Thread courant : main
main : 06:13:55
main : 06:13:56
main : 06:13:57
main : 06:13:58
main : 06:13:59
上一个示例说明了以下几点:
- Main 函数在单个线程中运行
- 我们可以通过 Thread.CurrentThread 访问该线程的属性
- Sleep 方法的作用。在此,执行 Main 的线程在每次显示之间会暂停 1 秒。
8.2. 创建执行线程
在某些应用程序中,代码片段可能在不同的执行线程中“同时”运行。当我们说线程同时运行时,通常只是泛泛而谈。如果机器只有一个处理器(这种情况至今仍很常见),则线程会共享该处理器:它们轮流在短暂的一瞬间(几毫秒)访问处理器。这正是产生并行执行错觉的原因。 分配给线程的时间长短取决于多种因素,包括其优先级——该优先级有默认值,但也可通过编程设置。当线程获得处理器时,通常会使用其分配的全部时间。不过,它也可以提前释放处理器:
- 通过等待某个事件(wait、join、suspend)
- 通过休眠指定时间(sleep)
- 线程 T 首先通过其构造函数创建
ThreadStart 属于委托类型,定义了一个无参数函数的原型:
典型的使用方式如下:
作为参数传递的 run 函数将在线程启动时被执行。
- 线程 T 的执行由 T.Start() 启动:此时,传递给 T 构造函数的 [run] 函数将由线程 T 执行。执行 T.Start() 语句的程序不会等待任务 T 完成:它会立即继续执行下一条语句。现在,我们有两个任务正在并行运行。它们通常需要相互通信,以了解待处理的共享工作的状态。这就是线程同步的问题。
- 线程一旦启动,便会自主运行。当其正在执行的启动函数完成工作后,线程即停止运行。
- 我们可以向任务 T 发送特定信号:
- T.Suspend() 命令其暂时暂停
- T.Resume() 指示其恢复工作
- T.Abort() 指示其永久停止
- 您还可以使用 T.join() 等待其执行完成。这是一条阻塞指令:执行该指令的程序将被阻塞,直到任务 T 完成工作。这是一种同步手段。
让我们来分析以下程序:
' options
Option Strict On
Option Explicit On
' namespaces
Imports System
Imports System.Threading
Module thread2
Public Sub Main()
' init Current thread
Dim main As Thread = Thread.CurrentThread
' name the Thread
main.Name = "main"
' creation of execution threads
Dim tâches(4) As Thread
Dim i As Integer
For i = 0 To tâches.Length - 1
' create thread i
tâches(i) = New Thread(New ThreadStart(AddressOf affiche))
' set the thread name
tâches(i).Name = "tache_" & i
' start execution of thread i
tâches(i).Start()
Next i
' end of hand
Console.Out.WriteLine(("fin du thread " + main.Name))
End Sub
Public Sub affiche()
' display start of execution
Console.Out.WriteLine(("Début d'exécution de la méthode affiche dans le Thread " + Thread.CurrentThread.Name + " : " + DateTime.Now.ToString("hh:mm:ss")))
' sleep for 1 s
Thread.Sleep(1000)
' display end of run
Console.Out.WriteLine(("Fin d'exécution de la méthode affiche dans le Thread " + Thread.CurrentThread.Name + " : " + DateTime.Now.ToString("hh:mm:ss")))
End Sub
End Module
主线程(即执行 Main 函数的线程)会创建另外 5 个线程,负责执行静态方法 display。结果如下:
dos>thread2
fin du thread main
Début d'exécution de la méthode affiche dans le Thread tache_0 : 05:27:53
Début d'exécution de la méthode affiche dans le Thread tache_1 : 05:27:53
Début d'exécution de la méthode affiche dans le Thread tache_2 : 05:27:53
Début d'exécution de la méthode affiche dans le Thread tache_3 : 05:27:53
Début d'exécution de la méthode affiche dans le Thread tache_4 : 05:27:53
Fin d'exécution de la méthode affiche dans le Thread tache_0 : 05:27:54
Fin d'exécution de la méthode affiche dans le Thread tache_1 : 05:27:54
Fin d'exécution de la méthode affiche dans le Thread tache_2 : 05:27:54
Fin d'exécution de la méthode affiche dans le Thread tache_3 : 05:27:54
Fin d'exécution de la méthode affiche dans le Thread tache_4 : 05:27:54
这些结果非常有参考价值:
- 首先,我们可以看到线程的启动并不阻塞。Main方法并行启动了5个线程的执行,并在它们完成执行之前就结束了自身。该
会启动线程 tasks[i] 的执行,但一旦启动,程序会立即继续执行下一条语句,而不会等待该线程完成。
- 所有创建的线程都必须执行 display 方法。执行顺序是不可预测的。尽管在示例中,执行顺序似乎遵循了执行请求的顺序,但无法由此得出普遍结论。此处的操作系统拥有 6 个线程和一个处理器。它将根据自身的规则将处理器分配给这 6 个线程。
- 结果显示了 Sleep 方法的影响。在示例中,线程 0 最先执行 display 方法。它先显示“开始执行”消息,随后执行 Sleep 方法,该方法使其暂停 1 秒。随后它失去处理器,处理器被释放给另一个线程。 示例表明线程 1 将获得处理器。线程 1 将遵循相同的执行路径,其他线程亦是如此。当线程 0 的 1 秒睡眠期结束时,其执行即可恢复。系统将处理器分配给它,它便能完成 display 方法的执行。
让我们修改程序,在 Main 方法结尾添加以下指令:
运行新程序后输出:
由 Main 函数创建的线程并未被执行。这是因为该语句
语句实现了这一效果:它终止了应用程序中的所有线程,而不仅仅是主线程。解决此问题的办法是让 Main 方法在终止自身之前,等待其创建的线程执行完毕。这可以通过使用 Thread 类的 Join 方法来实现:
' on attend la fin d'exécution de tous les threads
For i = 0 To tâches.Length - 1
' attente de la fin d'exécution du thread i
tâches(i).Join()
Next i 'for
' fin de main
Console.Out.WriteLine(("fin du thread " + main.Name))
Environment.Exit(0)
这将产生以下结果:
Début d'exécution de la méthode affiche dans le Thread tache_1 : 05:34:48
Début d'exécution de la méthode affiche dans le Thread tache_2 : 05:34:48
Début d'exécution de la méthode affiche dans le Thread tache_3 : 05:34:48
Début d'exécution de la méthode affiche dans le Thread tache_4 : 05:34:48
Début d'exécution de la méthode affiche dans le Thread tache_0 : 05:34:48
Fin d'exécution de la méthode affiche dans le Thread tache_2 : 05:34:50
Fin d'exécution de la méthode affiche dans le Thread tache_1 : 05:34:50
Fin d'exécution de la méthode affiche dans le Thread tache_3 : 05:34:50
Fin d'exécution de la méthode affiche dans le Thread tache_0 : 05:34:50
Fin d'exécution de la méthode affiche dans le Thread tache_4 : 05:34:50
fin du thread main
8.3. 线程的优势
既然我们已经指出了默认线程(即执行 Main 方法的那个)的存在,并且知道如何创建其他线程,那么让我们来探讨一下线程对我们的好处,以及为什么我们要在这里介绍它们。 有一种应用程序非常适合使用线程:互联网上的客户端-服务器应用程序。在这种应用程序中,位于机器 S1 上的服务器响应来自远程机器 C1、C2、...、Cn 上客户端的请求。
![]() |
我们每天都在使用遵循这种模式的互联网应用程序:Web 服务、电子邮件、论坛浏览、文件传输……在上图中,服务器 S1 必须同时为客户端 C1、C2、……、Cn 提供服务。以向客户端传输文件的 FTP(文件传输协议)服务器为例,我们知道文件传输有时可能需要数小时。 当然,单个客户端独占服务器如此长时间是绝无可能的。通常的做法是,服务器创建与客户端数量相等的执行线程。每个线程随后负责处理特定的客户端。由于处理器在机器上的所有活动线程之间循环共享,服务器在每个客户端上花费的时间都很短,从而确保了服务的并发性。
![]() |
8.4. 共享资源的访问
在上述客户端-服务器示例中,每个线程基本上独立地为一个客户端提供服务。尽管如此,线程可能需要协作才能向其客户端提供所请求的服务,特别是在访问共享资源时。上图类似于大型政府机构(如邮局)的柜台,每个柜台的职员为一名客户服务。假设这些职员不时需要复印客户带来的文件,而复印机只有一台。 两个工作人员无法同时使用复印机。如果工作人员 i 发现复印机正被工作人员 j 使用,则必须等待。这种情况被称为访问共享资源,在计算机科学中,其管理相当棘手。考虑以下示例:
- 一个应用程序将生成 n 个线程,其中 n 作为参数传入
- 共享资源是一个计数器,每个生成的线程都必须将其递增
- 在应用程序结束时,将显示计数器的值。因此,我们应得到 n。
程序代码如下:
' options
Option Explicit On
Option Strict On
' use of threads
Imports System
Imports System.Threading
Public Class thread3
' class variables
Private Shared cptrThreads As Integer = 0
Public Overloads Shared Sub Main(ByVal args() As [String])
' instructions for use
Const syntaxe As String = "pg nbThreads"
Const nbMaxThreads As Integer = 100
' verification no. of arguments
If args.Length <> 1 Then
' error
Console.Error.WriteLine(syntaxe)
' stop
Environment.Exit(1)
End If
' argument quality check
Dim nbThreads As Integer = 0
Try
nbThreads = Integer.Parse(args(0))
If nbThreads < 1 Or nbThreads > nbMaxThreads Then
Throw New Exception
End If
Catch
' error
Console.Error.WriteLine("Nombre de threads incorrect (entre 1 et " & nbMaxThreads & ")")
' end
Environment.Exit(2)
End Try
' thread creation and generation
Dim threads(nbThreads - 1) As Thread
Dim i As Integer
For i = 0 To nbThreads - 1
' creation
threads(i) = New Thread(New ThreadStart(AddressOf incrémente))
' naming
threads(i).Name = "tache_" & i
' launch
threads(i).Start()
Next i
' waiting for threads to finish
For i = 0 To nbThreads - 1
threads(i).Join()
Next i ' counter display
Console.Out.WriteLine(("Nombre de threads générés : " & cptrThreads))
End Sub
Public Shared Sub incrémente()
' increases thread counter
' meter reading
Dim valeur As Integer = cptrThreads
' follow-up
Console.Out.WriteLine(("A " + DateTime.Now.ToString("hh:mm:ss") & ", le thread " & Thread.CurrentThread.Name & " a lu la valeur du compteur : " & cptrThreads))
' waiting
Thread.Sleep(1000)
' counter incrementation
cptrThreads = valeur + 1
' follow-up
Console.Out.WriteLine(("A " & DateTime.Now.ToString("hh:mm:ss") & ", le thread " & Thread.CurrentThread.Name & " a écrit la valeur du compteur : " & cptrThreads))
End Sub
End Class
我们不再赘述线程创建部分,因为我们已经讲过了。相反,让我们关注 Increment 方法,每个线程都使用该方法来递增静态计数器 cptrThreads。
- 计数器被读取
- 线程暂停 1 秒。因此它会失去 CPU 控制权
- 计数器被递增
步骤 2 仅用于强制线程让出处理器。处理器将被分配给另一个线程。实际上,无法保证线程在读取计数器值与将其递增之间不会被中断。 在读取计数器值与写入加1后的值之间,存在失去CPU控制的风险。事实上,递增操作在处理器层面涉及多个基本指令,这些指令可能被中断。因此,步骤2(即一秒钟的睡眠)仅是为了应对这一风险。所得结果如下:
dos>thread3 5
A 05:44:34, le thread tache_0 a lu la valeur du compteur : 0
A 05:44:34, le thread tache_1 a lu la valeur du compteur : 0
A 05:44:34, le thread tache_2 a lu la valeur du compteur : 0
A 05:44:34, le thread tache_3 a lu la valeur du compteur : 0
A 05:44:34, le thread tache_4 a lu la valeur du compteur : 0
A 05:44:35, le thread tache_0 a écrit la valeur du compteur : 1
A 05:44:35, le thread tache_1 a écrit la valeur du compteur : 1
A 05:44:35, le thread tache_2 a écrit la valeur du compteur : 1
A 05:44:35, le thread tache_3 a écrit la valeur du compteur : 1
A 05:44:35, le thread tache_4 a écrit la valeur du compteur : 1
Nombre de threads générés : 1
从这些结果来看,情况很清楚:
- 第一个线程读取计数器。它发现值为 0。
- 它暂停 1 秒,从而将 CPU 让出
- 随后第二个线程获得 CPU 控制权,并读取计数器值。由于前一个线程尚未将其递增,该值仍为 0。该线程也暂停 1 秒。
- 在1秒内,所有5个线程都有时间运行并读取到值0。
- 当它们依次唤醒时,会将读取到的0加1,并将1写入计数器,这一结果由主程序(Main)确认。
问题出在哪里?第二个线程读取了错误的值,因为第一个线程在完成其任务(即更新窗口中的计数器)之前就被中断了。这引出了程序中关键资源和关键区段的概念:
- 关键资源是指一次只能由一个线程持有的资源。在此,关键资源即为计数器。
- 程序中的临界区是指线程执行流中访问临界资源的一段指令序列。我们必须确保在此临界区期间,只有该线程能够访问该资源。
8.5. 对共享资源的独占访问
在我们的示例中,临界区是位于读取计数器与写入其新值之间的代码段:
' meter reading
Dim valeur As Integer = cptrThreads
' waiting
Thread.Sleep(1000)
' counter incrementation
cptrThreads = valeur + 1
要执行这段代码,必须确保线程处于独占状态。该线程可能会被中断,但在中断期间,其他任何线程都不得执行这段相同的代码。 .NET 平台提供了多种工具来确保代码的关键部分仅由单个线程进入。我们将使用 Mutex 类:

在此,我们将仅使用以下构造函数和方法:
创建一个同步对象 M | |
执行 M.WaitOne() 操作的线程 T1 请求对同步对象 M 的所有权。如果互斥锁 M 未被任何线程持有(初始情况),则将其“授予”请求它的线程 T1。 如果稍后线程 T2 执行相同的操作,它将被阻塞。实际上,一个互斥锁只能属于一个线程。当线程 T1 释放其持有的互斥锁 M 时,该互斥锁将被释放。因此,多个线程在等待互斥锁 M 时可能会被阻塞。 | |
执行 M.ReleaseMutex() 操作的线程 T1 会放弃对互斥锁 M 的持有权。当线程 T1 失去处理器时,系统可以将其分配给正在等待互斥锁 M 的某个线程。只有一个线程会依次获得它;其余等待 M 的线程仍处于阻塞状态 |
互斥锁 M 管理对共享资源 R 的访问。线程通过 M.WaitOne() 请求资源 R,并通过 M.ReleaseMutex() 释放它。必须由单个线程在特定时间点执行的代码关键段即为共享资源。关键段执行的同步可通过以下方式实现:
其中 M 是一个互斥锁对象。当然,绝不能忘记释放不再需要的互斥锁,以便其他线程能够进入临界区;否则,等待一个永远不会被释放的互斥锁的线程将永远无法获得处理器访问权限。此外,必须避免出现两个线程相互等待的死锁情况。考虑以下按顺序发生的操作:
- 线程 T1 获取互斥锁 M1 的控制权以访问共享资源 R1
- 线程 T2 获取互斥锁 M2 以访问共享资源 R2
- 线程 T1 请求互斥锁 M2,被阻塞
- 线程 T2 请求互斥锁 M1,被阻塞。
在此,线程 T1 和 T2 处于相互等待的状态。当线程需要两个共享资源时,就会出现这种情况:一个是受互斥锁 M1 控制的资源 R1,另一个是受互斥锁 M2 控制的资源 R2。一种可能的解决方案是使用单个互斥锁 M 同时获取这两个资源。然而,如果这会导致对一个昂贵资源的长时间锁定,那么这种方法并不总是可行的。 另一种解决方案是:持有 M1 但无法获取 M2 的线程应释放 M1 以避免死锁。若将上述内容应用到前面的示例中,我们的应用程序将变为如下形式:
' options
Option Explicit On
Option Strict On
' use of threads
Imports System
Imports System.Threading
Public Class thread4
' class variables
Private Shared cptrThreads As Integer = 0 ' thread counter
Private Shared autorisation As Mutex
Public Overloads Shared Sub Main(ByVal args() As [String])
' instructions for use
Const syntaxe As String = "pg nbThreads"
Const nbMaxThreads As Integer = 100
' verification no. of arguments
If args.Length <> 1 Then
' error
Console.Error.WriteLine(syntaxe)
' stop
Environment.Exit(1)
End If
' argument quality check
Dim nbThreads As Integer = 0
Try
nbThreads = Integer.Parse(args(0))
If nbThreads < 1 Or nbThreads > nbMaxThreads Then
Throw New Exception
End If
Catch
End Try
' initialize access authorization to a critical section
autorisation = New Mutex
' thread creation and generation
Dim threads(nbThreads) As Thread
Dim i As Integer
For i = 0 To nbThreads - 1
' creation
threads(i) = New Thread(New ThreadStart(AddressOf incrémente))
' naming
threads(i).Name = "tache_" & i
' launch
threads(i).Start()
Next i
' waiting for threads to finish
For i = 0 To nbThreads - 1
threads(i).Join()
Next i
' counter display
Console.Out.WriteLine(("Nombre de threads générés : " & cptrThreads))
End Sub
Public Shared Sub incrémente()
' increases thread counter
' we request permission to enter the critical secton
autorisation.WaitOne()
' meter reading
Dim valeur As Integer = cptrThreads
' follow-up
Console.Out.WriteLine(("A " & DateTime.Now.ToString("hh:mm:ss") & ", le thread " & Thread.CurrentThread.Name & " a lu la valeur du compteur : " & cptrThreads))
' waiting
Thread.Sleep(1000)
' counter incrementation
cptrThreads = valeur + 1
' follow-up
Console.Out.WriteLine(("A " & DateTime.Now.ToString("hh:mm:ss") & ", le thread " & Thread.CurrentThread.Name & " a écrit la valeur du compteur : " & cptrThreads))
' access authorization is returned
autorisation.ReleaseMutex()
End Sub
End Class
所得结果与预期一致:
dos>thread4 5
A 05:51:10, le thread tache_0 a lu la valeur du compteur : 0
A 05:51:11, le thread tache_0 a écrit la valeur du compteur : 1
A 05:51:11, le thread tache_1 a lu la valeur du compteur : 1
A 05:51:12, le thread tache_1 a écrit la valeur du compteur : 2
A 05:51:12, le thread tache_2 a lu la valeur du compteur : 2
A 05:51:13, le thread tache_2 a écrit la valeur du compteur : 3
A 05:51:13, le thread tache_3 a lu la valeur du compteur : 3
A 05:51:14, le thread tache_3 a écrit la valeur du compteur : 4
A 05:51:14, le thread tache_4 a lu la valeur du compteur : 4
A 05:51:15, le thread tache_4 a écrit la valeur du compteur : 5
Nombre de threads générés : 5
8.6. 基于事件的同步
考虑以下情况,有时也被称为生产者-消费者场景。
- 我们有一个数组,其中一些进程向其中写入数据(生产者),而另一些进程则从中读取数据(消费者)。
- 生产者之间是平等的,但互斥的:同一时间只有一个生产者可以向数组中写入数据。
- 消费者之间是平等的,但互斥的:同一时间只有一个读取者可以读取数组中存储的数据。
- 只有当生产者将数据写入表中后,消费者才能从表中读取数据;只有当现有数据被消耗后,生产者才能将新数据写入表中。
在此解释中,我们可以区分两种共享资源:
- 可写表
- 只读数组
如前所述,对这两个共享资源的访问可以通过互斥锁进行控制,每个资源各配一个。一旦消费者获取了只读数组,它必须验证其中确实存在数据。将使用一个事件来通知它这一点。同样,获取了只读数组的生成者必须等待,直到消费者将其清空。这里也将使用一个事件。
所使用的事件将属于 AutoResetEvent 类:

此类事件类似于布尔值,但避免了主动或半主动等待。因此,如果写入访问由布尔值 *canWrite* 控制,生产者在写入前将执行如下代码:
或
在第一种方法中,线程不必要地占用了处理器资源。在第二种方法中,它每隔 100 毫秒检查一次 canWrite 布尔变量的状态。AutoResetEvent 类可以进一步优化:当线程等待的事件发生时,它会请求被唤醒:
AutoEvent peutEcrire=new AutoResetEvent(false) ' peutEcrire=false;
....
peutEcrire.WaitOne() ' thread waits for peutEcrire event to change to true
该操作
将 canWrite 布尔值初始化为 false。该操作
由一个线程执行时,如果布尔值 canWrite* 为 true,则该线程继续执行;否则,该线程将被阻塞,直到该值变为 true。另一个线程将使用 canWrite.Set() 操作将其设置为 true,或使用 canWrite.Reset()* 操作将其设置为 false。
生产者-消费者程序如下:
' use of reader and writer threads
' illustrates the simultaneous use of shared resources and synchronization
' options
Option Explicit On
Option Strict On
' use of threads
Imports System
Imports System.Threading
Public Class lececr
' class variables
Private Shared data(5) As Integer ' resource shared between reader and writer threads
Private Shared lecteur As Mutex ' synchronization variable to read the table
Private Shared écrivain As Mutex ' synchronization variable to write to the table
Private Shared objRandom As New Random(DateTime.Now.Second) ' a random number generator
Private Shared peutLire As AutoResetEvent ' indicates that you can read the contents of data
Private Shared peutEcrire As AutoResetEvent
Public Shared Sub Main(ByVal args() As [String])
' number of threads to generate
Const nbThreads As Integer = 3
' flag initialization
peutLire = New AutoResetEvent(False) ' cannot be read yet
peutEcrire = New AutoResetEvent(True) ' we can already write
' initialization of synchronization variables
lecteur = New Mutex ' synchronizes drives
écrivain = New Mutex ' synchronizes writers
' creation of reader threads
Dim lecteurs(nbThreads) As Thread
Dim i As Integer
For i = 0 To nbThreads - 1
' creation
lecteurs(i) = New Thread(New ThreadStart(AddressOf lire))
lecteurs(i).Name = "lecteur_" & i
' launch
lecteurs(i).Start()
Next i
' creating writer threads
Dim écrivains(nbThreads) As Thread
For i = 0 To nbThreads - 1
' creation
écrivains(i) = New Thread(New ThreadStart(AddressOf écrire))
écrivains(i).Name = "écrivain_" & i
' launch
écrivains(i).Start()
Next i
'end of hand
Console.Out.WriteLine("fin de Main...")
End Sub
' read the contents of the table
Public Shared Sub lire()
' review section
lecteur.WaitOne() ' a single reader can pass
peutLire.WaitOne() ' you must be able to read
' table reading
Dim i As Integer
For i = 0 To data.Length - 1
'wait 1 s
Thread.Sleep(1000)
' display
Console.Out.WriteLine((DateTime.Now.ToString("hh:mm:ss") & " : Le lecteur " & Thread.CurrentThread.Name & " a lu le nombre " & data(i)))
Next i
' we can no longer read
peutLire.Reset()
' we can write
peutEcrire.Set()
' end of critical section
lecteur.ReleaseMutex()
End Sub
' write in the table
Public Shared Sub écrire()
' review section
' only one writer can pass
écrivain.WaitOne()
' we have to wait for write authorization
peutEcrire.WaitOne()
' writing table
Dim i As Integer
For i = 0 To data.Length - 1
'wait 1 s
Thread.Sleep(1000)
' display
data(i) = objRandom.Next(0, 1000)
Console.Out.WriteLine((DateTime.Now.ToString("hh:mm:ss") & " : L'écrivain " & Thread.CurrentThread.Name & " a écrit le nombre " & data(i)))
Next i
' we can no longer write
peutEcrire.Reset()
' you can read
peutLire.Set()
'end of critical section
écrivain.ReleaseMutex()
End Sub
End Class
执行结果如下:
dos>lececr
fin de Main...
05:56:56 : L'écrivain écrivain_0 a écrit le nombre 459
05:56:57 : L'écrivain écrivain_0 a écrit le nombre 955
05:56:58 : L'écrivain écrivain_0 a écrit le nombre 212
05:56:59 : L'écrivain écrivain_0 a écrit le nombre 297
05:57:00 : L'écrivain écrivain_0 a écrit le nombre 37
05:57:01 : L'écrivain écrivain_0 a écrit le nombre 623
05:57:02 : Le lecteur lecteur_0 a lu le nombre 459
05:57:03 : Le lecteur lecteur_0 a lu le nombre 955
05:57:04 : Le lecteur lecteur_0 a lu le nombre 212
05:57:05 : Le lecteur lecteur_0 a lu le nombre 297
05:57:06 : Le lecteur lecteur_0 a lu le nombre 37
05:57:07 : Le lecteur lecteur_0 a lu le nombre 623
05:57:08 : L'écrivain écrivain_1 a écrit le nombre 549
05:57:09 : L'écrivain écrivain_1 a écrit le nombre 34
05:57:10 : L'écrivain écrivain_1 a écrit le nombre 781
05:57:11 : L'écrivain écrivain_1 a écrit le nombre 555
05:57:12 : L'écrivain écrivain_1 a écrit le nombre 812
05:57:13 : L'écrivain écrivain_1 a écrit le nombre 406
05:57:14 : Le lecteur lecteur_1 a lu le nombre 549
05:57:15 : Le lecteur lecteur_1 a lu le nombre 34
05:57:16 : Le lecteur lecteur_1 a lu le nombre 781
05:57:17 : Le lecteur lecteur_1 a lu le nombre 555
05:57:18 : Le lecteur lecteur_1 a lu le nombre 812
05:57:19 : Le lecteur lecteur_1 a lu le nombre 406
05:57:20 : L'écrivain écrivain_2 a écrit le nombre 442
05:57:21 : L'écrivain écrivain_2 a écrit le nombre 83
^C
可以注意到以下几点:
- 尽管在读取关键区会失去CPU,但确实每次只有一个读取者
- 确实每次只有一个写入者,尽管它在写入临界区会失去CPU
- 读取者仅在表中存在可读取内容时才进行读取
- 写入者仅在数组已完全读取完毕时才进行写入

