Freigeben über


Timer.Stop-Methode

Unterbricht das Auslösen des Elapsed-Ereignisses durch Festlegen von Enabled auf false.

Namespace: System.Timers
Assembly: System (in system.dll)

Syntax

'Declaration
Public Sub Stop
'Usage
Dim instance As Timer

instance.Stop
public void Stop ()
public:
void Stop ()
public void Stop ()
public function Stop ()

Hinweise

Sie können die zeitliche Steuerung auch unterbrechen, indem Sie Enabled auf false festlegen.

Hinweis

Das Elapsed-Ereignis wird in einem ThreadPool-Thread ausgelöst. Deshalb kann die Ereignisbehandlungsmethode in einem Thread ausgeführt werden, während ein Aufruf an die Stop-Methode in einem anderen Thread ausgeführt wird. In der Folge kann Elapsed-Ereignis nach dem Aufruf der Stop-Methode ausgelöst werden. Im Codebeispiel zu diesem Thema wird eine Möglichkeit gezeigt, die Racebedingung zu verhindern.

Beispiel

Im folgenden Codebeispiel wird eine Möglichkeit gezeigt, den Thread, der die Stop-Methode aufruft, anzuhalten, bis ein aktuell ausgeführtes Elapsed-Ereignis beendet ist, und außerdem zu verhindern, dass zwei Elapsed-Ereignisse den Ereignishandler gleichzeitig ausführen (häufig als Reentranz bezeichnet).

Im Beispiel werden 100 Testläufe ausgeführt. Jedes Mal, wenn der Test ausgeführt wird, wird der Zeitgeber mit einem Intervall von 150 Millisekunden gestartet. Der Ereignishandler verwendet die Thread.Sleep-Methode, um eine Aufgabe zu simulieren, deren Länge nach dem Zufallsprinzip zwischen 50 und 200 Millisekunden variiert. Die Testmethode startet außerdem einen Steuerthread, der 1 Sekunde wartet und dann den Zeitgeber anhält. Wenn ein Ereignis behandelt wird, während der Steuerthread den Zeitgeber anhält, muss der Steuerthread warten, bis das Ereignis beendet ist, und kann dann erst fortfahren.

Die Überladung der Interlocked.CompareExchange(Int32,Int32,Int32)-Methode wird verwendet, um Reentranz zu vermeiden und zu verhindern, dass der Steuerthread fortfährt, bevor ein ausgeführtes Ereignis beendet ist. Der Ereignishandler verwendet die CompareExchange(Int32,Int32,Int32)-Methode, um eine Steuerelementvariable auf 1 festzulegen, aber nur, wenn der Wert momentan 0 (null) beträgt. Dies ist ein atomarer Vorgang. Wenn der Rückgabewert 0 (null) beträgt, wurde die Steuerelementvariable auf 1 festgelegt, und der Ereignishandler fährt fort. Wenn der Rückgabewert ungleich 0 (null) ist, wird das Ereignis zur Vermeidung von Reentranz einfach verworfen. (Wenn die Notwendigkeit besteht, jedes Ereignis auszuführen, bietet die Monitor-Klasse eine bessere Möglichkeit zur Synchronisierung der Ereignisse.) Wenn der Ereignishandler beendet ist, legt er die Steuerelementvariable wieder auf 0 (null) fest. Im Beispiel ist die Gesamtanzahl der Ereignisse aufgeführt, die ausgeführt wurden, die wegen Reentranz verworfen wurden und die nach dem Aufruf der Stop-Methode aufgetreten sind.

Der Steuerthread verwendet die CompareExchange(Int32,Int32,Int32)-Methode, um die Steuerelementvariable auf -1 (minus eins) festzulegen, aber nur, wenn der Wert momentan 0 (null) beträgt. Wenn der atomare Vorgang einen Wert ungleich 0 (null) zurückgibt, wird momentan ein Ereignis ausgeführt. Der Steuerthread wartet und wiederholt den Vorgang. Im Beispiel ist angegeben, wie oft der Steuerthread die Beendigung eines Ereignisses abwarten musste.

Imports System
Imports System.Timers
Imports System.Threading

Public Module Test
    
    ' Change these values to control the behavior of the program.
    Private testRuns As Integer = 100 
    ' Times are given in milliseconds:
    Private testRunsFor As Integer = 1000
    Private timerInterval As Integer = 150

    ' Qualify the name to avoid confusion with the
    ' System.Threading.Timer class.
    Private WithEvents Timer1 As New System.Timers.Timer
    Private rand As New Random()

    ' This is the synchronization point that prevents events
    ' from running concurrently, and prevents the main thread 
    ' from executing code after the Stop method until any 
    ' event handlers are done executing.
    Private syncPoint As Integer = 0

    ' Count the number of times the event handler is called,
    ' is executed, is skipped, or is called after Stop.
    Private numEvents As Integer = 0
    Private numExecuted As Integer = 0
    Private numSkipped As Integer = 0
    Private numLate As Integer = 0

    ' Count the number of times the thread that calls Stop
    ' has to wait for an Elapsed event to finish.
    Private numWaits As Integer = 0

    <MTAThread> _
    Sub Main()
        Timer1.Interval = timerInterval

        Console.WriteLine()
        For i As Integer = 1 To testRuns
            TestRun
            Console.Write(vbCr & "Test {0}/{1}    ", i, testRuns)
        Next

        Console.WriteLine("{0} test runs completed.", testRuns)
        Console.WriteLine("{0} events were raised.", numEvents)
        Console.WriteLine("{0} events executed.", numExecuted)
        Console.WriteLine("{0} events were skipped for concurrency.", numSkipped)
        Console.WriteLine("{0} events were skipped because they were late.", numLate)
        Console.WriteLine("Control thread waited {0} times for an event to complete.", numWaits)
    End Sub

    Sub TestRun()
        ' Set syncPoint to zero before starting the test 
        ' run. 
        syncPoint = 0

        Timer1.Enabled = True

        ' Start the control thread that shuts off the timer.
        Dim t As New Thread(AddressOf ControlThreadProc)
        t.Start()

        ' Wait until the control thread is done before proceeding.
        ' This keeps the test runs from overlapping.
        t.Join()

    End Sub

    Private Sub ControlThreadProc()
        ' Allow the timer to run for a period of time, and then 
        ' stop it.
        Thread.Sleep(testRunsFor) 
        Timer1.Stop

        ' The 'counted' flag ensures that if this thread has
        ' to wait for an event to finish, the wait only gets 
        ' counted once.
        Dim counted As Boolean = False

        ' Ensure that if an event is currently executing,
        ' no further processing is done on this thread until
        ' the event handler is finished. This is accomplished
        ' by using CompareExchange to place -1 in syncPoint,
        ' but only if syncPoint is currently zero (specified
        ' by the third parameter of CompareExchange). 
        ' CompareExchange returns the original value that was
        ' in syncPoint. If it was not zero, then there's an
        ' event handler running, and it is necessary to try
        ' again.
        While Interlocked.CompareExchange(syncPoint, -1, 0) <> 0 
            ' Give up the rest of this thread's current time
            ' slice. This is a fairly naive algorithm for 
            ' yielding.
            Thread.Sleep(0)

            ' Tally a wait, but don't count multiple calls to
            ' Thread.Sleep.
            If Not counted Then
                numWaits += 1
                counted = True
            End If
        End While

        ' Any processing done after this point does not conflict
        ' with timer events. This is the purpose of the call to
        ' CompareExchange. If the processing done here would not
        ' cause a problem when run concurrently with timer events,
        ' then there is no need for the extra synchronization.
    End Sub

    ' Event-handling methof for the Elapsed event.
    Private Sub Timer1_ElapsedEventHandler( _
        ByVal sender As Object, _
        ByVal e As ElapsedEventArgs _
      ) Handles Timer1.Elapsed

        numEvents += 1

        ' This example assumes that overlapping events can be
        ' discarded. That is, if an Elapsed event is raised before 
        ' the previous event is finished processing, the second
        ' event is ignored. 
        '
        ' CompareExchange is used to take control of syncPoint, 
        ' and to determine whether the attempt was successful. 
        ' CompareExchange attempts to put 1 into syncPoint, but
        ' only if the current value of syncPoint is zero 
        ' (specified by the third parameter). If another thread
        ' has set syncPoint to 1, or if the control thread has
        ' set syncPoint to -1, the current event is skipped. 
        ' (Normally it would not be necessary to use a local 
        ' variable for the return value. A local variable is 
        ' used here to determine the reason the event was 
        ' skipped.)
        '
        Dim sync As Integer = Interlocked.CompareExchange(syncPoint, 1, 0)
        If sync = 0 Then
            ' No other event was executing.
            ' The event handler simulates an amount of work
            ' lasting between 50 and 200 milliseconds, so that
            ' some events will overlap.
            Dim delay As Integer = 50 + rand.Next(150)
            Thread.Sleep(delay)
            numExecuted += 1

            ' Release control of syncPoint.
            syncPoint = 0
        Else
            If sync = 1 Then numSkipped += 1 Else numLate += 1
        End If
    End Sub 

End Module

' On a dual-processor computer, this code example produces
' results similar to the following:
'
'Test 100/100    100 test runs completed.
'600 events were raised.
'488 events executed.
'112 events were skipped for concurrency.
'0 events were skipped because they were late.
'Control thread waited 73 times for an event to complete.
using System;
using System.Timers;
using System.Threading;

public class Test
{    
    // Change these values to control the behavior of the program.
    private static int testRuns = 100;
    // Times are given in milliseconds:
    private static int testRunsFor = 1000;
    private static int timerInterval = 150;

    // Qualify the name to avoid confusion with the
    // System.Threading.Timer class.
    private static System.Timers.Timer Timer1 = new System.Timers.Timer();
    private static Random rand = new Random();

    // This is the synchronization point that prevents events
    // from running concurrently, and prevents the main thread 
    // from executing code after the Stop method until any 
    // event handlers are done executing.
    private static int syncPoint = 0;

    // Count the number of times the event handler is called,
    // is executed, is skipped, or is called after Stop.
    private static int numEvents = 0;
    private static int numExecuted = 0;
    private static int numSkipped = 0;
    private static int numLate = 0;

    // Count the number of times the thread that calls Stop
    // has to wait for an Elapsed event to finish.
    private static int numWaits = 0;

    [MTAThread]
    public static void Main()
    {
        Timer1.Elapsed += new ElapsedEventHandler(Timer1_ElapsedEventHandler);
        Timer1.Interval = timerInterval;

        Console.WriteLine();
        for(int i = 1; i <= testRuns; i++)
        {
            TestRun();
            Console.Write("\rTest {0}/{1}    ", i, testRuns);
        }

        Console.WriteLine("{0} test runs completed.", testRuns);
        Console.WriteLine("{0} events were raised.", numEvents);
        Console.WriteLine("{0} events executed.", numExecuted);
        Console.WriteLine("{0} events were skipped for concurrency.", numSkipped);
        Console.WriteLine("{0} events were skipped because they were late.", numLate);
        Console.WriteLine("Control thread waited {0} times for an event to complete.", numWaits);
    }

    public static void TestRun()
    {
        // Set syncPoint to zero before starting the test 
        // run. 
        syncPoint = 0;

        Timer1.Enabled = true;

        // Start the control thread that shuts off the timer.
        Thread t = new Thread(ControlThreadProc);
        t.Start();

        // Wait until the control thread is done before proceeding.
        // This keeps the test runs from overlapping.
        t.Join();

    }

    private static void ControlThreadProc()
    {
        // Allow the timer to run for a period of time, and then 
        // stop it.
        Thread.Sleep(testRunsFor);
        Timer1.Stop();

        // The 'counted' flag ensures that if this thread has
        // to wait for an event to finish, the wait only gets 
        // counted once.
        bool counted = false;

        // Ensure that if an event is currently executing,
        // no further processing is done on this thread until
        // the event handler is finished. This is accomplished
        // by using CompareExchange to place -1 in syncPoint,
        // but only if syncPoint is currently zero (specified
        // by the third parameter of CompareExchange). 
        // CompareExchange returns the original value that was
        // in syncPoint. If it was not zero, then there's an
        // event handler running, and it is necessary to try
        // again.
        while (Interlocked.CompareExchange(ref syncPoint, -1, 0) != 0)
        {
            // Give up the rest of this thread's current time
            // slice. This is a fairly naive algorithm for 
            // yielding.
            Thread.Sleep(0);

            // Tally a wait, but don't count multiple calls to
            // Thread.Sleep.
            if (!counted)
            {
                numWaits += 1;
                counted = true;
            }
        }

        // Any processing done after this point does not conflict
        // with timer events. This is the purpose of the call to
        // CompareExchange. If the processing done here would not
        // cause a problem when run concurrently with timer events,
        // then there is no need for the extra synchronization.
    }

    // Event-handling method for the Elapsed event.
    private static void Timer1_ElapsedEventHandler(
        object sender, 
        ElapsedEventArgs e)
    {
        numEvents += 1;

        // This example assumes that overlapping events can be
        // discarded. That is, if an Elapsed event is raised before 
        // the previous event is finished processing, the second
        // event is ignored. 
        //
        // CompareExchange is used to take control of syncPoint, 
        // and to determine whether the attempt was successful. 
        // CompareExchange attempts to put 1 into syncPoint, but
        // only if the current value of syncPoint is zero 
        // (specified by the third parameter). If another thread
        // has set syncPoint to 1, or if the control thread has
        // set syncPoint to -1, the current event is skipped. 
        // (Normally it would not be necessary to use a local 
        // variable for the return value. A local variable is 
        // used here to determine the reason the event was 
        // skipped.)
        //
        int sync = Interlocked.CompareExchange(ref syncPoint, 1, 0);
        if (sync == 0)
        {
            // No other event was executing.
            // The event handler simulates an amount of work
            // lasting between 50 and 200 milliseconds, so that
            // some events will overlap.
            int delay = 50 + rand.Next(150);
            Thread.Sleep(delay);
            numExecuted += 1;

            // Release control of syncPoint.
            syncPoint = 0;
        }
        else
        {
            if (sync == 1) { numSkipped += 1; } else { numLate += 1; }
        }
    }
}

/* On a dual-processor computer, this code example produces
   results similar to the following:

Test 100/100    100 test runs completed.
600 events were raised.
488 events executed.
112 events were skipped for concurrency.
0 events were skipped because they were late.
Control thread waited 73 times for an event to complete.
 */

Plattformen

Windows 98, Windows 2000 SP4, Windows Millennium Edition, Windows Server 2003, Windows XP Media Center Edition, Windows XP Professional x64 Edition, Windows XP SP2, Windows XP Starter Edition

.NET Framework unterstützt nicht alle Versionen sämtlicher Plattformen. Eine Liste der unterstützten Versionen finden Sie unter Systemanforderungen.

Versionsinformationen

.NET Framework

Unterstützt in: 2.0, 1.1, 1.0

Siehe auch

Referenz

Timer-Klasse
Timer-Member
System.Timers-Namespace
Enabled
Start
Elapsed