Timer [C#]
计时器是使您能够指定要在指定时间调用的委托的轻量对象。线程池中的线程执行等待操作。
使用 Timer 类是非常简单的。需要创建一个 Timer(通过将 TimerCallback 委托传递到回调方法)、一个表示将被传递给回调的状态的对象、初始引发时间以及表示回调调用之间的时间段的时间。若要取消挂起的计时器,请调用 Timer.Dispose 函数。
注意 还有 System.Windows.Forms.Timer 类。该类是基于操作系统计时器支持的,如果您没有在线程上发送消息,则计时器将不会出现。这就使得 System.Threading.Timer 在许多情况下更为有用。
下面的简单代码示例说明了 Timer 的使用。
[Visual Basic]
Imports System
Imports System.Threading
Imports Microsoft.VisualBasic
Public Class TimerTest
Public timerevent As ManualResetEvent
Public Sub New()
timerevent = New ManualResetEvent(False)
Dim timer As New Timer(New TimerCallback(AddressOf Me.TimerMethod), Nothing, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5))
Dim TickTimer As New Timer(New TimerCallback(AddressOf Me.Tick), Nothing, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1))
End Sub 'New
Public Sub TimerMethod(state As Object)
Console.WriteLine(ControlChars.Cr + "The Timer invoked this method.")
timerevent.Set()
End Sub 'TimerMethod
Public Sub Tick(state As Object)
Console.Write(".")
End Sub 'Tick
Public Shared Sub Main()
Dim test As New TimerTest()
Console.WriteLine("The timer has started and will count for five seconds.")
test.timerevent.WaitOne()
Console.WriteLine("...and control returned to the primary thread.")
End Sub 'Main
End Class 'TimerTest
[C#]
using System;
using System.Threading;
public class TimerTest{
public ManualResetEvent timerevent;
public TimerTest(){
timerevent = new ManualResetEvent(false);
Timer timer = new Timer(
new TimerCallback(this.TimerMethod),
null,
TimeSpan.FromSeconds(5),
TimeSpan.FromSeconds(5)
);
Timer TickTimer = new Timer(
new TimerCallback(this.Tick),
null,
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(1)
);
}
public void TimerMethod(object state){
Console.WriteLine("\rThe Timer invoked this method.");
timerevent.Set();
}
public void Tick(object state){
Console.Write(".");
}
public static void Main(){
TimerTest test = new TimerTest();
Console.WriteLine("The timer has started and will count for five seconds.");
test.timerevent.WaitOne();
Console.WriteLine("...and control returned to the primary thread.");
}
}
Monitor [C#]
Monitor 对象通过使用 Monitor.Enter、Monitor.TryEnter 和 Monitor.Exit 方法对特定对象获取锁和释放锁来公开同步访问代码区域的能力。在对代码区域获取锁后,就可以使用 Monitor.Wait、Monitor.Pulse 和 Monitor.PulseAll 方法了。如果锁被暂挂,则 Wait 释放该锁并等待通知。当 Wait 接到通知后,它将返回并再次获取该锁。Pulse 和 PulseAll 都会发出信号以便等待队列中的下一个线程继续执行。
注意到 Monitor 和 WaitHandle 对象在使用上的区别是非常重要的。Monitor 对象是完全托管、完全可移植的,并且在操作系统资源要求方面可能更为有效。WaitHandle 对象表示操作系统可等待对象,对于在托管和非托管代码之间进行同步非常有用,并公开一些高级操作系统功能(如同时等待许多对象的能力)。
下面的代码示例说明了 Monitor 类(用编译器关键字实现)和 Interlocked 类的结合使用。
[Visual Basic]
Imports System
Imports System.Threading
Imports Microsoft.VisualBasic
' Note: The class whose internal public member is the synchronizing method
' is not public; none of the client code takes a lock on the Resource object.
' The member of the nonpublic class takes the lock on itself. Written this
' way, malicious code cannot take a lock on a public object.
Class SyncResource
Public Sub Access(threadNum As Int32)
' Uses Monitor class to enforce synchronization.
SyncLock Me
' Synchronized: Despite the next conditional, each thread waits on its predecessor.
If threadNum Mod 2 = 0 Then
Thread.Sleep(2000)
End If
Console.WriteLine("Start Synched Resource access (Thread={0})", threadNum)
Thread.Sleep(200)
Console.WriteLine("Stop Synched Resource access (Thread={0})", threadNum)
End SyncLock
End Sub 'Access
End Class 'SyncResource
' Without the lock, the method is called in the order in which threads reach it.
Class UnSyncResource
Public Sub Access(threadNum As Int32)
' Does not use Monitor class to enforce synchronization.
' The next call throws the thread order.
If threadNum Mod 2 = 0 Then
Thread.Sleep(2000)
End If
Console.WriteLine("Start UnSynched Resource access (Thread={0})", threadNum)
Thread.Sleep(200)
Console.WriteLine("Stop UnSynched Resource access (Thread={0})", threadNum)
End Sub 'Access
End Class 'UnSyncResource
Public Class App
Private Shared numAsyncOps As Int32 = 5
Private Shared asyncOpsAreDone As New AutoResetEvent(False)
Private Shared SyncRes As New SyncResource()
Private Shared UnSyncRes As New UnSyncResource()
Private Shared threadNum As Int32
Public Shared Sub Main()
For threadNum = 0 To 4
ThreadPool.QueueUserWorkItem(AddressOf SyncUpdateResource, threadNum)
Next threadNum
' Wait until this WaitHandle is signaled.
asyncOpsAreDone.WaitOne()
Console.WriteLine(ControlChars.Tab + ControlChars.Lf + "All synchronized operations have completed." + ControlChars.Lf)
' Reset the thread count for unsynchronized calls.
numAsyncOps = 5
For threadNum = 0 To 4
ThreadPool.QueueUserWorkItem(AddressOf UnSyncUpdateResource, threadNum)
Next threadNum
' Wait until this WaitHandle is signaled.
asyncOpsAreDone.WaitOne()
Console.WriteLine(ControlChars.Tab + ControlChars.Cr + "All unsynchronized thread operations have completed.")
End Sub 'Main
' The callback method's signature MUST match that of a System.Threading.TimerCallback
' delegate (it takes an Object parameter and returns void).
Shared Sub SyncUpdateResource(state As Object)
' This calls the internal synchronized method, passing a thread number.
SyncRes.Access(CType(state, Int32))
' Count down the number of methods that the threads have called.
' This must be synchronized, however; you cannot know which thread will
' access the value **before** another thread's incremented value has been
' stored into the variable.
If Interlocked.Decrement(numAsyncOps) = 0 Then
asyncOpsAreDone.Set()
' Announce to Main that in fact all thread calls are done.
End If
End Sub 'SyncUpdateResource
' The callback method's signature MUST match that of a System.Threading.TimerCallback
' delegate (it takes an Object parameter and returns void).
Shared Sub UnSyncUpdateResource(state As [Object])
' This calls the internal synchronized method, passing a thread number.
UnSyncRes.Access(CType(state, Int32))
' Count down the number of methods that the threads have called.
' This must be synchronized, however; you cannot know which thread will
' access the value **before** another thread's incremented value has been
' stored into the variable.
If Interlocked.Decrement(numAsyncOps) = 0 Then
asyncOpsAreDone.Set()
' Announce to Main that in fact all thread calls are done.
End If
End Sub 'UnSyncUpdateResource
End Class 'App
[C#]
using System;
using System.Threading;
// Note: The class whose internal public member is the synchronizing method
// is not public; none of the client code takes a lock on the Resource object.
// The member of the nonpublic class takes the lock on itself. Written this
// way, malicious code cannot take a lock on a public object.
class SyncResource {
public void Access(Int32 threadNum) {
// Uses Monitor class to enforce synchronization.
lock (this) {
// Synchronized: Despite the next conditional, each thread waits on its predecessor.
if (threadNum % 2 == 0)
Thread.Sleep(2000);
Console.WriteLine("Start Synched Resource access (Thread={0})", threadNum);
Thread.Sleep(200);
Console.WriteLine("Stop Synched Resource access (Thread={0})", threadNum);
}
}
}
// Without the lock, the method is called in the order in which threads reach it.
class UnSyncResource {
public void Access(Int32 threadNum) {
// Does not use Monitor class to enforce synchronization.
// The next call throws the thread order.
if (threadNum % 2 == 0)
Thread.Sleep(2000);
Console.WriteLine("Start UnSynched Resource access (Thread={0})", threadNum);
Thread.Sleep(200);
Console.WriteLine("Stop UnSynched Resource access (Thread={0})", threadNum);
}
}
public class App {
static Int32 numAsyncOps = 5;
static AutoResetEvent asyncOpsAreDone = new AutoResetEvent(false);
static SyncResource SyncRes = new SyncResource();
static UnSyncResource UnSyncRes = new UnSyncResource();
public static void Main() {
for (Int32 threadNum = 0; threadNum < 5; threadNum++) {
ThreadPool.QueueUserWorkItem(new WaitCallback(SyncUpdateResource), threadNum);
}
// Wait until this WaitHandle is signaled.
asyncOpsAreDone.WaitOne();
Console.WriteLine("\t\nAll synchronized operations have completed.\t\n");
// Reset the thread count for unsynchronized calls.
numAsyncOps = 5;
for (Int32 threadNum = 0; threadNum < 5; threadNum++) {
ThreadPool.QueueUserWorkItem(new WaitCallback(UnSyncUpdateResource), threadNum);
}
// Wait until this WaitHandle is signaled.
asyncOpsAreDone.WaitOne();
Console.WriteLine("\t\nAll unsynchronized thread operations have completed.");
}
// The callback method's signature MUST match that of a System.Threading.TimerCallback
// delegate (it takes an Object parameter and returns void).
static void SyncUpdateResource(Object state) {
// This calls the internal synchronized method, passing a thread number.
SyncRes.Access((Int32) state);
// Count down the number of methods that the threads have called.
// This must be synchronized, however; you cannot know which thread will
// access the value **before** another thread's incremented value has been
// stored into the variable.
if (Interlocked.Decrement(ref numAsyncOps) == 0)
asyncOpsAreDone.Set();
// Announce to Main that in fact all thread calls are done.
}
// The callback method's signature MUST match that of a System.Threading.TimerCallback
// delegate (it takes an Object parameter and returns void).
static void UnSyncUpdateResource(Object state) {
// This calls the internal synchronized method, passing a thread number.
UnSyncRes.Access((Int32) state);
// Count down the number of methods that the threads have called.
// This must be synchronized, however; you cannot know which thread will
// access the value **before** another thread's incremented value has been
// stored into the variable.
if (Interlocked.Decrement(ref numAsyncOps) == 0)
asyncOpsAreDone.Set();
// Announce to Main that in fact all thread calls are done.
}
}
WaitHandle
WaitHandle 类封装 Win32 同步句柄,并用于表示运行库中所有允许执行多个等待操作的同步对象。需要注意的是,虽然 WaitHandle 对象表示操作系统同步对象并因此而公开高级功能,但它们还是不如 Monitor 的可移植性好;Monitor 是完全托管的,某些情况下在使用操作系统资源方面更为有效。
ManualResetEvent
使用 ManualResetEvent 类使某个线程等待,直到某个事件通过调用 ManualResetEvent.Set 将其置于已发信号的状态为止。ManualResetEvent 将始终保持已发信号的状态,直到通过 Reset 方法将其显式设置为未发信号的状态为止。对于任何数目的等待线程或随后通过调用一个等待函数开始等待指定的事件对象的操作的线程,它们都可以在该对象处于已发信号的状态时被释放。ManualResetEvent 与 Win32 CreateEvent 调用相对应,并将 bManualReset 参数指定为 true。
AutoResetEvent
使用 AutoResetEvent 类可以使某个线程等待,直到某个事件通过调用 AutoResetEvent.Set 将其置于已发信号的状态为止。与 ManualResetEvent 不同,AutoResetEvent 在单个等待线程被释放后由系统自动重置为未发信号的状态。如果没有任何线程正在等待,则该事件对象的状态将保持已发信号的状态。AutoResetEvent 与 Win32 CreateEvent 调用相对应,并将 bManualReset 参数指定为 false。