System.Threading.Thread class

This article provides supplementary remarks to the reference documentation for this API.

The Thread class creates and controls a thread, sets its priority, and gets its status.

When a process starts, the common language runtime automatically creates a single foreground thread to execute application code. Along with this main foreground thread, a process can create one or more threads to execute a portion of the program code associated with the process. These threads can execute either in the foreground or in the background. In addition, you can use the ThreadPool class to execute code on worker threads that are managed by the common language runtime.

Start a thread

You start a thread by supplying a delegate that represents the method the thread is to execute in its class constructor. You then call the Start method to begin execution.

The Thread constructors can take either of two delegate types, depending on whether you can pass an argument to the method to be executed:

  • If the method has no arguments, you pass a ThreadStart delegate to the constructor. It has the signature:

    public delegate void ThreadStart()
    
    Public Delegate Sub ThreadStart()
    

    The following example creates and starts a thread that executes the ExecuteInForeground method. The method displays information about some thread properties, then executes a loop in which it pauses for half a second and displays the elapsed number of seconds. When the thread has executed for at least five seconds, the loop ends and the thread terminates execution.

    using System;
    using System.Diagnostics;
    using System.Threading;
    
    public class Example2
    {
       public static void Main()
       {
          var th = new Thread(ExecuteInForeground);
          th.Start();
          Thread.Sleep(1000);
          Console.WriteLine("Main thread ({0}) exiting...",
                            Thread.CurrentThread.ManagedThreadId);
       }
    
       private static void ExecuteInForeground()
       {
          var sw = Stopwatch.StartNew();
          Console.WriteLine("Thread {0}: {1}, Priority {2}",
                            Thread.CurrentThread.ManagedThreadId,
                            Thread.CurrentThread.ThreadState,
                            Thread.CurrentThread.Priority);
          do {
             Console.WriteLine("Thread {0}: Elapsed {1:N2} seconds",
                               Thread.CurrentThread.ManagedThreadId,
                               sw.ElapsedMilliseconds / 1000.0);
             Thread.Sleep(500);
          } while (sw.ElapsedMilliseconds <= 5000);
          sw.Stop();
       }
    }
    // The example displays output like the following:
    //       Thread 3: Running, Priority Normal
    //       Thread 3: Elapsed 0.00 seconds
    //       Thread 3: Elapsed 0.51 seconds
    //       Main thread (1) exiting...
    //       Thread 3: Elapsed 1.02 seconds
    //       Thread 3: Elapsed 1.53 seconds
    //       Thread 3: Elapsed 2.05 seconds
    //       Thread 3: Elapsed 2.55 seconds
    //       Thread 3: Elapsed 3.07 seconds
    //       Thread 3: Elapsed 3.57 seconds
    //       Thread 3: Elapsed 4.07 seconds
    //       Thread 3: Elapsed 4.58 seconds
    
    open System.Diagnostics
    open System.Threading
    
    let executeInForeground () =
        let sw = Stopwatch.StartNew()
    
        printfn $"Thread {Thread.CurrentThread.ManagedThreadId}: {Thread.CurrentThread.ThreadState}, Priority {Thread.CurrentThread.Priority}"
    
        while sw.ElapsedMilliseconds <= 5000 do
            printfn $"Thread {Thread.CurrentThread.ManagedThreadId}: Elapsed {sw.ElapsedMilliseconds / 1000L:N2} seconds"
            Thread.Sleep 500
    
        sw.Stop()
    
    let th = Thread executeInForeground
    th.Start()
    Thread.Sleep 1000
    printfn $"Main thread ({Thread.CurrentThread.ManagedThreadId}) exiting..."
    
    // The example displays output like the following:
    //       Thread 3: Running, Priority Normal
    //       Thread 3: Elapsed 0.00 seconds
    //       Thread 3: Elapsed 0.51 seconds
    //       Main thread (1) exiting...
    //       Thread 3: Elapsed 1.02 seconds
    //       Thread 3: Elapsed 1.53 seconds
    //       Thread 3: Elapsed 2.05 seconds
    //       Thread 3: Elapsed 2.55 seconds
    //       Thread 3: Elapsed 3.07 seconds
    //       Thread 3: Elapsed 3.57 seconds
    //       Thread 3: Elapsed 4.07 seconds
    //       Thread 3: Elapsed 4.58 seconds
    
    Imports System.Diagnostics
    Imports System.Threading
    
    Module Example3
        Public Sub Main()
            Dim th As New Thread(AddressOf ExecuteInForeground)
            th.Start()
            Thread.Sleep(1000)
            Console.WriteLine("Main thread ({0}) exiting...", Thread.CurrentThread.ManagedThreadId)
        End Sub
    
        Private Sub ExecuteInForeground()
            Dim start As DateTime = DateTime.Now
            Dim sw As Stopwatch = Stopwatch.StartNew()
            Console.WriteLine("Thread {0}: {1}, Priority {2}",
                            Thread.CurrentThread.ManagedThreadId,
                            Thread.CurrentThread.ThreadState,
                            Thread.CurrentThread.Priority)
            Do
                Console.WriteLine("Thread {0}: Elapsed {1:N2} seconds",
                               Thread.CurrentThread.ManagedThreadId,
                               sw.ElapsedMilliseconds / 1000)
                Thread.Sleep(500)
            Loop While sw.ElapsedMilliseconds <= 5000
            sw.Stop()
        End Sub
    End Module
    ' The example displays output like the following:
    '       Thread 3: Running, Priority Normal
    '       Thread 3: Elapsed 0.00 seconds
    '       Thread 3: Elapsed 0.51 seconds
    '       Main thread (1) exiting...
    '       Thread 3: Elapsed 1.02 seconds
    '       Thread 3: Elapsed 1.53 seconds
    '       Thread 3: Elapsed 2.05 seconds
    '       Thread 3: Elapsed 2.55 seconds
    '       Thread 3: Elapsed 3.07 seconds
    '       Thread 3: Elapsed 3.57 seconds
    '       Thread 3: Elapsed 4.07 seconds
    '       Thread 3: Elapsed 4.58 seconds
    
  • If the method has an argument, you pass a ParameterizedThreadStart delegate to the constructor. It has the signature:

    public delegate void ParameterizedThreadStart(object obj)
    
    Public Delegate Sub ParameterizedThreadStart(obj As Object)
    

    The method executed by the delegate can then cast (in C#) or convert (in Visual Basic) the parameter to the appropriate type.

    The following example is identical to the previous one, except that it calls the Thread(ParameterizedThreadStart) constructor. This version of the ExecuteInForeground method has a single parameter that represents the approximate number of milliseconds the loop is to execute.

    using System;
    using System.Diagnostics;
    using System.Threading;
    
    public class Example3
    {
       public static void Main()
       {
          var th = new Thread(ExecuteInForeground);
          th.Start(4500);
          Thread.Sleep(1000);
          Console.WriteLine("Main thread ({0}) exiting...",
                            Thread.CurrentThread.ManagedThreadId);
       }
    
       private static void ExecuteInForeground(Object obj)
       {
          int interval;
          try {
             interval = (int) obj;
          }
          catch (InvalidCastException) {
             interval = 5000;
          }
          var sw = Stopwatch.StartNew();
          Console.WriteLine("Thread {0}: {1}, Priority {2}",
                            Thread.CurrentThread.ManagedThreadId,
                            Thread.CurrentThread.ThreadState,
                            Thread.CurrentThread.Priority);
          do {
             Console.WriteLine("Thread {0}: Elapsed {1:N2} seconds",
                               Thread.CurrentThread.ManagedThreadId,
                               sw.ElapsedMilliseconds / 1000.0);
             Thread.Sleep(500);
          } while (sw.ElapsedMilliseconds <= interval);
          sw.Stop();
       }
    }
    // The example displays output like the following:
    //       Thread 3: Running, Priority Normal
    //       Thread 3: Elapsed 0.00 seconds
    //       Thread 3: Elapsed 0.52 seconds
    //       Main thread (1) exiting...
    //       Thread 3: Elapsed 1.03 seconds
    //       Thread 3: Elapsed 1.55 seconds
    //       Thread 3: Elapsed 2.06 seconds
    //       Thread 3: Elapsed 2.58 seconds
    //       Thread 3: Elapsed 3.09 seconds
    //       Thread 3: Elapsed 3.61 seconds
    //       Thread 3: Elapsed 4.12 seconds
    
    open System
    open System.Diagnostics
    open System.Threading
    
    let executeInForeground obj =
        let interval =
            try
                unbox<int> obj
            with :? InvalidCastException ->
                5000
    
        let sw = Stopwatch.StartNew()
    
        printfn $"Thread {Thread.CurrentThread.ManagedThreadId}: {Thread.CurrentThread.ThreadState}, Priority {Thread.CurrentThread.Priority}"
    
        while sw.ElapsedMilliseconds <= interval do
            printfn $"Thread {Thread.CurrentThread.ManagedThreadId}: Elapsed {sw.ElapsedMilliseconds / 1000L:N2} seconds"
            Thread.Sleep 500
    
        sw.Stop()
    
    let th = Thread(ParameterizedThreadStart executeInForeground)
    th.Start 4500
    Thread.Sleep 1000
    printfn $"Main thread ({Thread.CurrentThread.ManagedThreadId}) exiting..."
    
    // The example displays output like the following:
    //       Thread 3: Running, Priority Normal
    //       Thread 3: Elapsed 0.00 seconds
    //       Thread 3: Elapsed 0.52 seconds
    //       Main thread (1) exiting...
    //       Thread 3: Elapsed 1.03 seconds
    //       Thread 3: Elapsed 1.55 seconds
    //       Thread 3: Elapsed 2.06 seconds
    //       Thread 3: Elapsed 2.58 seconds
    //       Thread 3: Elapsed 3.09 seconds
    //       Thread 3: Elapsed 3.61 seconds
    //       Thread 3: Elapsed 4.12 seconds
    
    Imports System.Diagnostics
    Imports System.Threading
    
    Module Example4
        Public Sub Main()
            Dim th As New Thread(AddressOf ExecuteInForeground)
            th.Start(4500)
            Thread.Sleep(1000)
            Console.WriteLine("Main thread ({0}) exiting...", Thread.CurrentThread.ManagedThreadId)
        End Sub
    
        Private Sub ExecuteInForeground(obj As Object)
            Dim interval As Integer
            If IsNumeric(obj) Then
                interval = CInt(obj)
            Else
                interval = 5000
            End If
            Dim start As DateTime = DateTime.Now
            Dim sw As Stopwatch = Stopwatch.StartNew()
            Console.WriteLine("Thread {0}: {1}, Priority {2}",
                            Thread.CurrentThread.ManagedThreadId,
                            Thread.CurrentThread.ThreadState,
                            Thread.CurrentThread.Priority)
            Do
                Console.WriteLine("Thread {0}: Elapsed {1:N2} seconds",
                               Thread.CurrentThread.ManagedThreadId,
                               sw.ElapsedMilliseconds / 1000)
                Thread.Sleep(500)
            Loop While sw.ElapsedMilliseconds <= interval
            sw.Stop()
        End Sub
    End Module
    ' The example displays output like the following:
    '       Thread 3: Running, Priority Normal
    '       Thread 3: Elapsed 0.00 seconds
    '       Thread 3: Elapsed 0.52 seconds
    '       Main thread (1) exiting...
    '       Thread 3: Elapsed 1.03 seconds
    '       Thread 3: Elapsed 1.55 seconds
    '       Thread 3: Elapsed 2.06 seconds
    '       Thread 3: Elapsed 2.58 seconds
    '       Thread 3: Elapsed 3.09 seconds
    '       Thread 3: Elapsed 3.61 seconds
    '       Thread 3: Elapsed 4.12 seconds
    

It isn't necessary to retain a reference to a Thread object once you've started the thread. The thread continues to execute until the thread procedure is complete.

Retrieve Thread objects

You can use the static (Shared in Visual Basic) CurrentThread property to retrieve a reference to the currently executing thread from the code that the thread is executing. The following example uses the CurrentThread property to display information about the main application thread, another foreground thread, a background thread, and a thread pool thread.

using System;
using System.Threading;

public class Example1
{
   static Object obj = new Object();
   
   public static void Main()
   {
      ThreadPool.QueueUserWorkItem(ShowThreadInformation);
      var th1 = new Thread(ShowThreadInformation);
      th1.Start();
      var th2 = new Thread(ShowThreadInformation);
      th2.IsBackground = true;
      th2.Start();
      Thread.Sleep(500);
      ShowThreadInformation(null); 
   }
   
   private static void ShowThreadInformation(Object state)
   {
      lock (obj) {
         var th  = Thread.CurrentThread;
         Console.WriteLine("Managed thread #{0}: ", th.ManagedThreadId);
         Console.WriteLine("   Background thread: {0}", th.IsBackground);
         Console.WriteLine("   Thread pool thread: {0}", th.IsThreadPoolThread);
         Console.WriteLine("   Priority: {0}", th.Priority);
         Console.WriteLine("   Culture: {0}", th.CurrentCulture.Name);
         Console.WriteLine("   UI culture: {0}", th.CurrentUICulture.Name);
         Console.WriteLine();
      }   
   }
}
// The example displays output like the following:
//       Managed thread #6:
//          Background thread: True
//          Thread pool thread: False
//          Priority: Normal
//          Culture: en-US
//          UI culture: en-US
//       
//       Managed thread #3:
//          Background thread: True
//          Thread pool thread: True
//          Priority: Normal
//          Culture: en-US
//          UI culture: en-US
//       
//       Managed thread #4:
//          Background thread: False
//          Thread pool thread: False
//          Priority: Normal
//          Culture: en-US
//          UI culture: en-US
//       
//       Managed thread #1:
//          Background thread: False
//          Thread pool thread: False
//          Priority: Normal
//          Culture: en-US
//          UI culture: en-US
open System.Threading

let obj = obj ()

let showThreadInformation (state: obj) =
    lock obj (fun () ->
        let th = Thread.CurrentThread
        printfn $"Managed thread #{th.ManagedThreadId}: "
        printfn $"   Background thread: {th.IsBackground}"
        printfn $"   Thread pool thread: {th.IsThreadPoolThread}"
        printfn $"   Priority: {th.Priority}"
        printfn $"   Culture: {th.CurrentCulture.Name}"
        printfn $"   UI culture: {th.CurrentUICulture.Name}"
        printfn "")

ThreadPool.QueueUserWorkItem showThreadInformation |> ignore
let th1 = Thread(ParameterizedThreadStart showThreadInformation)
th1.Start()
let th2 = Thread(ParameterizedThreadStart showThreadInformation)
th2.IsBackground <- true
th2.Start()
Thread.Sleep 500
showThreadInformation ()

// The example displays output like the following:
//       Managed thread #6:
//          Background thread: True
//          Thread pool thread: False
//          Priority: Normal
//          Culture: en-US
//          UI culture: en-US
//
//       Managed thread #3:
//          Background thread: True
//          Thread pool thread: True
//          Priority: Normal
//          Culture: en-US
//          UI culture: en-US
//
//       Managed thread #4:
//          Background thread: False
//          Thread pool thread: False
//          Priority: Normal
//          Culture: en-US
//          UI culture: en-US
//
//       Managed thread #1:
//          Background thread: False
//          Thread pool thread: False
//          Priority: Normal
//          Culture: en-US
//          UI culture: en-US
Imports System.Threading

Module Example2
    Private lock As New Object()

    Public Sub Main()
        ThreadPool.QueueUserWorkItem(AddressOf ShowThreadInformation)
        Dim th1 As New Thread(AddressOf ShowThreadInformation)
        th1.Start()
        Dim th2 As New Thread(AddressOf ShowThreadInformation)
        th2.IsBackground = True
        th2.Start()
        Thread.Sleep(500)
        ShowThreadInformation(Nothing)
    End Sub

    Private Sub ShowThreadInformation(state As Object)
        SyncLock lock
            Dim th As Thread = Thread.CurrentThread
            Console.WriteLine("Managed thread #{0}: ", th.ManagedThreadId)
            Console.WriteLine("   Background thread: {0}", th.IsBackground)
            Console.WriteLine("   Thread pool thread: {0}", th.IsThreadPoolThread)
            Console.WriteLine("   Priority: {0}", th.Priority)
            Console.WriteLine("   Culture: {0}", th.CurrentCulture.Name)
            Console.WriteLine("   UI culture: {0}", th.CurrentUICulture.Name)
            Console.WriteLine()
        End SyncLock
    End Sub
End Module
' The example displays output like the following:
'       ' Managed thread #6:
'          Background thread: True
'          Thread pool thread: False
'          Priority: Normal
'          Culture: en-US
'          UI culture: en-US
'       
'       Managed thread #3:
'          Background thread: True
'          Thread pool thread: True
'          Priority: Normal
'          Culture: en-US
'          UI culture: en-US
'       
'       Managed thread #4:
'          Background thread: False
'          Thread pool thread: False
'          Priority: Normal
'          Culture: en-US
'          UI culture: en-US
'       
'       Managed thread #1:
'          Background thread: False
'          Thread pool thread: False
'          Priority: Normal
'          Culture: en-US
'          UI culture: en-US

Foreground and background threads

Instances of the Thread class represent either foreground threads or background threads. Background threads are identical to foreground threads with one exception: a background thread does not keep a process running if all foreground threads have terminated. Once all foreground threads have been stopped, the runtime stops all background threads and shuts down.

By default, the following threads execute in the foreground:

  • The main application thread.

  • All threads created by calling a Thread class constructor.

The following threads execute in the background by default:

  • Thread pool threads, which come from a pool of worker threads maintained by the runtime. You can configure the thread pool and schedule work on thread pool threads by using the ThreadPool class.

    Note

    Task-based asynchronous operations automatically execute on thread pool threads. Task-based asynchronous operations use the Task and Task<TResult> classes to implement the task-based asynchronous pattern.

  • All threads that enter the managed execution environment from unmanaged code.

You can change a thread to execute in the background by setting the IsBackground property at any time. Background threads are useful for any operation that should continue as long as an application is running but should not prevent the application from terminating, such as monitoring file system changes or incoming socket connections.

The following example illustrates the difference between foreground and background threads. It's like the first example in the Start a thread section, except that it sets the thread to execute in the background before starting it. As the output shows, the loop is interrupted before it executes for five seconds.

using System;
using System.Diagnostics;
using System.Threading;

public class Example
{
   public static void Main()
   {
      var th = new Thread(ExecuteInForeground);
      th.IsBackground = true;
      th.Start();
      Thread.Sleep(1000);
      Console.WriteLine("Main thread ({0}) exiting...",
                        Thread.CurrentThread.ManagedThreadId);
   }

   private static void ExecuteInForeground()
   {
      var sw = Stopwatch.StartNew();
      Console.WriteLine("Thread {0}: {1}, Priority {2}",
                        Thread.CurrentThread.ManagedThreadId,
                        Thread.CurrentThread.ThreadState,
                        Thread.CurrentThread.Priority);
      do {
         Console.WriteLine("Thread {0}: Elapsed {1:N2} seconds",
                           Thread.CurrentThread.ManagedThreadId,
                           sw.ElapsedMilliseconds / 1000.0);
         Thread.Sleep(500);
      } while (sw.ElapsedMilliseconds <= 5000);
      sw.Stop();
   }
}
// The example displays output like the following:
//       Thread 3: Background, Priority Normal
//       Thread 3: Elapsed 0.00 seconds
//       Thread 3: Elapsed 0.51 seconds
//       Main thread (1) exiting...
open System.Diagnostics
open System.Threading

let executeInForeground () =
    let sw = Stopwatch.StartNew()
    printfn $"Thread {Thread.CurrentThread.ManagedThreadId}: {Thread.CurrentThread.ThreadState}, Priority {Thread.CurrentThread.Priority}"
    while sw.ElapsedMilliseconds <= 5000 do
        printfn $"Thread {Thread.CurrentThread.ManagedThreadId}: Elapsed {sw.ElapsedMilliseconds / 1000L:N2} seconds"
        Thread.Sleep 500
    sw.Stop()

let th = Thread executeInForeground
th.IsBackground <- true
th.Start()
Thread.Sleep 1000
printfn $"Main thread ({Thread.CurrentThread.ManagedThreadId}) exiting..."

// The example displays output like the following:
//       Thread 3: Background, Priority Normal
//       Thread 3: Elapsed 0.00 seconds
//       Thread 3: Elapsed 0.51 seconds
//       Main thread (1) exiting...
Imports System.Diagnostics
Imports System.Threading

Module Example1
    Public Sub Main()
        Dim th As New Thread(AddressOf ExecuteInForeground)
        th.IsBackground = True
        th.Start()
        Thread.Sleep(1000)
        Console.WriteLine("Main thread ({0}) exiting...", Thread.CurrentThread.ManagedThreadId)
    End Sub

    Private Sub ExecuteInForeground()
        Dim start As DateTime = DateTime.Now
        Dim sw As Stopwatch = Stopwatch.StartNew()
        Console.WriteLine("Thread {0}: {1}, Priority {2}",
                        Thread.CurrentThread.ManagedThreadId,
                        Thread.CurrentThread.ThreadState,
                        Thread.CurrentThread.Priority)
        Do
            Console.WriteLine("Thread {0}: Elapsed {1:N2} seconds",
                           Thread.CurrentThread.ManagedThreadId,
                           sw.ElapsedMilliseconds / 1000)
            Thread.Sleep(500)
        Loop While sw.ElapsedMilliseconds <= 5000
        sw.Stop()
    End Sub
End Module
' The example displays output like the following:
'       Thread 3: Background, Priority Normal
'       Thread 3: Elapsed 0.00 seconds
'       Thread 3: Elapsed 0.51 seconds
'       Main thread (1) exiting...

Culture and threads

Each thread has a culture, represented by the CurrentCulture property, and a UI culture, represented by the CurrentUICulture property. The current culture supports culture-sensitive operations, such as parsing and formatting, string comparison, and sorting, and also controls the writing system and calendar used by a thread. The current UI culture provides for culture-sensitive retrieval of resources in resource files.

Important

The CurrentCulture and CurrentUICulture properties don't work reliably when used with any thread other than the current thread. In .NET Framework, reading these properties is reliable, although setting these properties for a thread other than the current thread is not. On .NET Core, an InvalidOperationException is thrown if a thread attempts to read or write these properties on a different thread. We recommend that you use the CultureInfo.CurrentCulture and CultureInfo.CurrentUICulture properties to retrieve and set the current culture.

When a new thread is instantiated, its culture and UI culture are defined by the current system culture and UI culture, and not by the culture and UI culture of the thread from which the new thread is created. This means, for example, that if the current system culture is English (United States) and the current culture of the primary application thread is French (France), the culture of a new thread created by calling the Thread(ParameterizedThreadStart) constructor from the primary thread is English (United States), and not French (France). For more information, see the "Culture and threads" section of the CultureInfo class topic.

Important

This is not true of threads that execute asynchronous operations for apps that target .NET Framework 4.6 and later versions. In this case, the culture and UI culture is part of an asynchronous operation's context; the thread on which an asynchronous operation executes by default inherits the culture and UI culture of the thread from which the asynchronous operation was launched. For more information, see the "Culture and task-based asynchronous operations" section of the CultureInfo class remarks.

You can do either of the following to ensure that all of the threads executing in an application share the same culture and UI culture:

For more information and examples, see the "Culture and threads" section of the CultureInfo class remarks.

Get information about and control threads

You can retrieve a number of property values that provide information about a thread. In some cases, you can also set these property values to control the operation of the thread. These thread properties include:

  • A name. Name is a write-once property that you can use to identify a thread. Its default value is null.

  • A hash code, which you can retrieve by calling the GetHashCode method. The hash code can be used to uniquely identify a thread; for the lifetime of your thread, its hash code will not collide with the value from any other thread, regardless of the application domain from which you obtain the value.

  • A thread ID. The value of the read-only ManagedThreadId property is assigned by the runtime and uniquely identifies a thread within its process.

    Note

    An operating-system ThreadId has no fixed relationship to a managed thread, because an unmanaged host can control the relationship between managed and unmanaged threads. Specifically, a sophisticated host can use the CLR Hosting API to schedule many managed threads against the same operating system thread, or to move a managed thread between different operating system threads.

  • The thread's current state. For the duration of its existence, a thread is always in one or more of the states defined by the ThreadState property.

  • A scheduling priority level, which is defined by the ThreadPriority property. Although you can set this value to request a thread's priority, it is not guaranteed to be honored by the operating system.

  • The read-only IsThreadPoolThread property, which indicates whether a thread is a thread-pool thread.

  • The IsBackground property. For more information, see the Foreground and background threads section.

Examples

The following example demonstrates simple threading functionality.

using System;
using System.Threading;

// Simple threading scenario:  Start a static method running
// on a second thread.
public class ThreadExample {
    // The ThreadProc method is called when the thread starts.
    // It loops ten times, writing to the console and yielding
    // the rest of its time slice each time, and then ends.
    public static void ThreadProc() {
        for (int i = 0; i < 10; i++) {
            Console.WriteLine("ThreadProc: {0}", i);
            // Yield the rest of the time slice.
            Thread.Sleep(0);
        }
    }

    public static void Main() {
        Console.WriteLine("Main thread: Start a second thread.");
        // The constructor for the Thread class requires a ThreadStart
        // delegate that represents the method to be executed on the
        // thread.  C# simplifies the creation of this delegate.
        Thread t = new Thread(new ThreadStart(ThreadProc));

        // Start ThreadProc.  Note that on a uniprocessor, the new
        // thread does not get any processor time until the main thread
        // is preempted or yields.  Uncomment the Thread.Sleep that
        // follows t.Start() to see the difference.
        t.Start();
        //Thread.Sleep(0);

        for (int i = 0; i < 4; i++) {
            Console.WriteLine("Main thread: Do some work.");
            Thread.Sleep(0);
        }

        Console.WriteLine("Main thread: Call Join(), to wait until ThreadProc ends.");
        t.Join();
        Console.WriteLine("Main thread: ThreadProc.Join has returned.  Press Enter to end program.");
        Console.ReadLine();
    }
}
open System.Threading

// Simple threading scenario:  Start a static method running
// on a second thread.

// The ThreadProc method is called when the thread starts.
// It loops ten times, writing to the console and yielding
// the rest of its time slice each time, and then ends.
let threadProc () =
    for i = 0 to 9 do
        printfn $"ThreadProc: {i}"
        // Yield the rest of the time slice.
        Thread.Sleep 0

printfn "Main thread: Start a second thread."
// The constructor for the Thread class requires a ThreadStart
// delegate that represents the method to be executed on the
// thread. F# simplifies the creation of this delegate.
let t = Thread threadProc

// Start ThreadProc.  Note that on a uniprocessor, the new
// thread does not get any processor time until the main thread
// is preempted or yields.  Uncomment the Thread.Sleep that
// follows t.Start() to see the difference.
t.Start()
//Thread.Sleep 0

for _ = 0 to 3 do
    printfn "Main thread: Do some work."
    Thread.Sleep 0

printfn "Main thread: Call Join(), to wait until ThreadProc ends."
t.Join()
printfn "Main thread: ThreadProc.Join has returned.  Press Enter to end program."
stdin.ReadLine() |> ignore
Imports System.Threading

' Simple threading scenario:  Start a Shared method running
' on a second thread.
Public Class ThreadExample
    ' The ThreadProc method is called when the thread starts.
    ' It loops ten times, writing to the console and yielding 
    ' the rest of its time slice each time, and then ends.
    Public Shared Sub ThreadProc()
        Dim i As Integer
        For i = 0 To 9
            Console.WriteLine("ThreadProc: {0}", i)
            ' Yield the rest of the time slice.
            Thread.Sleep(0)
        Next
    End Sub

    Public Shared Sub Main()
        Console.WriteLine("Main thread: Start a second thread.")
        ' The constructor for the Thread class requires a ThreadStart 
        ' delegate.  The Visual Basic AddressOf operator creates this
        ' delegate for you.
        Dim t As New Thread(AddressOf ThreadProc)

        ' Start ThreadProc.  Note that on a uniprocessor, the new 
        ' thread does not get any processor time until the main thread 
        ' is preempted or yields.  Uncomment the Thread.Sleep that 
        ' follows t.Start() to see the difference.
        t.Start()
        'Thread.Sleep(0)

        Dim i As Integer
        For i = 1 To 4
            Console.WriteLine("Main thread: Do some work.")
            Thread.Sleep(0)
        Next

        Console.WriteLine("Main thread: Call Join(), to wait until ThreadProc ends.")
        t.Join()
        Console.WriteLine("Main thread: ThreadProc.Join has returned.  Press Enter to end program.")
        Console.ReadLine()
    End Sub
End Class

This code produces output similar to the following:

[VB, C++, C#]
Main thread: Start a second thread.
Main thread: Do some work.
ThreadProc: 0
Main thread: Do some work.
ThreadProc: 1
Main thread: Do some work.
ThreadProc: 2
Main thread: Do some work.
ThreadProc: 3
Main thread: Call Join(), to wait until ThreadProc ends.
ThreadProc: 4
ThreadProc: 5
ThreadProc: 6
ThreadProc: 7
ThreadProc: 8
ThreadProc: 9
Main thread: ThreadProc.Join has returned. Press Enter to end program.