Condividi tramite


Creating Threads and Passing Data at Start Time

Microsoft Silverlight will reach end of support after October 2021. Learn more.

When a Silverlight-based application starts, the currently executing thread is the user interface thread. You can determine the thread that is executing code at any given time by retrieving the static Thread.CurrentThread property (Shared property in Visual Basic). This topic describes the creation of additional threads, and discusses alternatives for passing data to the thread procedure.

Creating a Thread

Creating a new Thread object creates a new managed thread. The Thread class has constructors that take a ThreadStart delegate or a ParameterizedThreadStart delegate; the delegate wraps the method that is invoked by the new thread when you call the Start method. Calling Start more than once causes a ThreadStateException to be thrown.

The Start method returns immediately, often before the new thread has actually started. You can use the ThreadState and IsAlive properties to determine the state of the thread at any one moment, but these properties should never be used for synchronizing the activities of threads.

NoteNote:

Once a thread is started, it is not necessary to retain a reference to the Thread object. The thread continues to execute until the thread procedure ends.

The following example shows how to execute a static method on a new thread. To run this example, see Building Examples That Use a Demo Method and a TextBlock Control.

Imports System.Threading

Public Class Example

   Private Shared outputBlock As System.Windows.Controls.TextBlock

   Public Shared Sub Demo(ByVal outputBlock As System.Windows.Controls.TextBlock)

      Example.outputBlock = outputBlock

      ' To start a thread using a static thread procedure, use the
      ' class name and method name when you create the ThreadStart
      ' delegate. Visual Basic expands the AddressOf expression 
      ' to the appropriate delegate creation syntax:
      '    New ThreadStart(AddressOf Example.DoWork)
      '
      Dim newThread As New Thread(AddressOf Example.DoWork)
      newThread.Start()

   End Sub

   ' Simulate work. To communicate with objects on the UI thread, get the 
   ' Dispatcher for one of the UI objects. Use the Dispatcher object's 
   ' BeginInvoke method to queue a delegate that will run on the UI thread,
   ' and therefore can safely access UI elements like the TextBlock.
   Private Shared Sub DoWork()
      Dim display As New Action(Of String)(AddressOf DisplayOutput)

      outputBlock.Dispatcher.BeginInvoke(display, _
         "Hello from a Shared thread procedure." & vbCrLf)
   End Sub

   ' The Dispatcher.BeginInvoke method runs this helper method on the 
   ' UI thread, so it can safely access the TextBlock that is used to 
   ' display the output.
   Private Shared Sub DisplayOutput(msg)
      outputBlock.Text &= msg
   End Sub

End Class

' This code example produces the following output:
'
'Hello from a Shared thread procedure.
using System;
using System.Threading;

public class Example
{
   private static System.Windows.Controls.TextBlock outputBlock;

   public static void Demo(System.Windows.Controls.TextBlock outputBlock)
   {
      Example.outputBlock = outputBlock;

      // To start a thread using a static thread procedure, use the
      // class name and method name when you create the ThreadStart
      // delegate. C# expands the method name to the appropriate 
      // delegate creation syntax:
      //    New ThreadStart(Example.DoWork)
      //
      Thread newThread = new Thread(Example.DoWork);
      newThread.Start();
   }

   // Simulate work. To communicate with objects on the UI thread, get the 
   // Dispatcher for one of the UI objects. Use the Dispatcher object's 
   // BeginInvoke method to queue a delegate that will run on the UI thread,
   // and therefore can safely access UI elements like the TextBlock.
   private static void DoWork()
   {
      outputBlock.Dispatcher.BeginInvoke(delegate () { 
         outputBlock.Text += "Hello from a static thread procedure.\n"; 
      });
   }
}

/* This code example produces the following output:

Hello from a static thread procedure.
 */

The following example shows how to execute an instance method on a new thread. To run this example, see Building Examples That Use a Demo Method and a TextBlock Control.

Imports System.Threading

Public Class Example

   Public Shared Sub Demo(ByVal outputBlock As System.Windows.Controls.TextBlock)

      ' To start a thread using an instance method for the thread 
      ' procedure, use the instance variable and method name when 
      ' you create the ThreadStart delegate. Visual Basic expands 
      ' the AddressOf expression to the appropriate delegate 
      ' creation syntax:
      '    New ThreadStart(AddressOf w.DoMoreWork)
      '
      Dim w As New Work()
      w.Data = 42
      w.Output = outputBlock

      Dim newThread As Thread = New Thread(AddressOf w.DoMoreWork)
      newThread.Start()
   End Sub

End Class

Public Class Work
   Public Data As Integer
   Public Output As System.Windows.Controls.TextBlock

   ' Simulate work. To communicate with objects on the UI thread, get the 
   ' Dispatcher for one of the UI objects. Use the Dispatcher object's 
   ' BeginInvoke method to queue a delegate that will run on the UI thread,
   ' and therefore can safely access UI elements like the TextBlock.
   Public Sub DoMoreWork()
      Dim display As New Action(Of String)(AddressOf DisplayOutput)

      Output.Dispatcher.BeginInvoke(display, _
         String.Format("Instance thread procedure. Data={0}", Data) & vbCrLf)
   End Sub

   ' The Dispatcher.BeginInvoke method runs this helper method on the 
   ' UI thread, so it can safely access the TextBlock that is used to 
   ' display the output.
   Private Sub DisplayOutput(msg)
      Output.Text &= msg
   End Sub

End Class

' This code example produces the following output:
'
'Instance thread procedure. Data=42
using System;
using System.Threading;

public class Example
{
   public static void Demo(System.Windows.Controls.TextBlock outputBlock)
   {
      // To start a thread using an instance method for the thread 
      // procedure, use the instance variable and method name when 
      // you create the ThreadStart delegate. C# expands the object
      // reference and method name to the appropriate delegate 
      // creation syntax:
      //    New ThreadStart(AddressOf w.DoMoreWork)
      //
      Work w = new Work();
      w.Data = 42;
      w.Output = outputBlock;

      Thread newThread = new Thread(w.DoMoreWork);
      newThread.Start();
   }
}

public class Work
{
   public int Data;
   public System.Windows.Controls.TextBlock Output;

   // Simulate work. To communicate with objects on the UI thread, get the 
   // Dispatcher for one of the UI objects. Use the Dispatcher object's 
   // BeginInvoke method to queue a delegate that will run on the UI thread,
   // and therefore can safely access UI elements like the TextBlock.
   public void DoMoreWork()
   {
      Output.Dispatcher.BeginInvoke(delegate () {
         Output.Text += String.Format("Instance thread procedure. Data={0}\n", Data);
      });
   }
}

// This code example produces the following output:
//
//Instance thread procedure. Data=42

Passing Data to Threads and Retrieving Data from Threads

If you are running background tasks from the user interface thread, the BackgroundWorker class provides a simple, event-driven model for communicating between the background thread and the user interface thread. See How to: Use a Background Worker.

The ParameterizedThreadStart delegate provides an easy way to pass an object that contains data to a thread: Simply pass the object to the Start(Object) method overload. See ParameterizedThreadStart for a code example.

Using the ParameterizedThreadStart delegate is not a type-safe way to pass data, because the Start(Object) method overload accepts any object. An alternative is to encapsulate the thread procedure and the data in a helper class and use the ThreadStart delegate to execute the thread procedure. This technique is shown in the two examples in the following sections.

The ThreadStart and ParameterizedThreadStart delegates do not have return values, because there is no place to return the data from an asynchronous call. To retrieve the results of a thread method, you can get the data from the object that encapsulates the data, as shown in the example in the Retrieving Data by Polling section, or allow the thread to call back with the results. In a Silverlight-based application this might mean making a cross-thread callback to the user interface thread, as shown in the second example, in the Retrieving Data with Callback Methods section.

Important noteImportant Note:

In Silverlight-based applications, avoid making blocking calls in code that runs on the user interface thread. If the user interface thread waits for another thread by calling Join or other methods that block, your application becomes unresponsive.

Retrieving Data by Polling

The following example defines a ThreadWithState class that contains both the data and the thread procedure. The result of the thread procedure is stored in an internal field (Friend field in Visual Basic) where it can be retrieved by code that is running on the user interface thread. In this example, the user interface code that polls for the result is in a mouse button event.

To run this example, see Building Examples That Use a Demo Method and a TextBlock Control.

Imports System.Threading

' The ThreadWithState class contains the information needed for
' a task, and the method that executes the task.
'
Public Class ThreadWithState
    ' State information used in the task.
    Private boilerplate As String
    Private value As Integer

    Public Finished As Boolean = False
    Public Result As String = Nothing

    ' The constructor obtains the state information.
    Public Sub New(ByVal text As String, ByVal number As Integer)
        boilerplate = text
        value = number
    End Sub

    ' The thread procedure performs the task, such as formatting 
    ' and printing a document.
    Public Sub ThreadProc()
        Thread.Sleep(3000)
        Result = String.Format(boilerplate, value) 
        Finished = True
    End Sub


End Class

' Entry point for the example.
'
Public Class Example

    Private Shared outputBlock As System.Windows.Controls.TextBlock
    Private Shared tws As ThreadWithState

    Public Shared Sub Demo(ByVal outputBlock As System.Windows.Controls.TextBlock)

        Example.outputBlock = outputBlock
        outputBlock.Text = "Click here to start the thread." & vbLf 

        AddHandler outputBlock.MouseLeftButtonUp, AddressOf MouseUpStart

    End Sub

    Private Shared Sub MouseUpStart(ByVal sender As Object, _
                                    ByVal e As MouseButtonEventArgs)

        RemoveHandler outputBlock.MouseLeftButtonUp, AddressOf MouseUpStart

        ' Supply the state information required by the task.
        tws = New ThreadWithState("This report displays the number {0}.", 42)

        ' Create a thread to execute the task, and then
        ' start the thread.
        Dim t As New Thread(AddressOf tws.ThreadProc)
        t.Start()

        outputBlock.Text = "Click here to check for the result." & vbLf
        AddHandler outputBlock.MouseLeftButtonUp, AddressOf MouseUp

    End Sub

    Private Shared Sub MouseUp(ByVal sender As Object, _
                               ByVal e As MouseButtonEventArgs)

        If tws.Finished Then
            RemoveHandler outputBlock.MouseLeftButtonUp, AddressOf MouseUp
            outputBlock.Text &= tws.Result
        Else
            outputBlock.Text &= "Not done yet." & vbLf
        End If

    End Sub
End Class
using System;
using System.Threading;

// The ThreadWithState class contains the information needed for
// a task, and the method that executes the task.
//
public class ThreadWithState
{
    // State information used in the task.
    private string boilerplate;
    private int value;

    public bool Finished = false;
    public string Result = null;

    // The constructor obtains the state information.
    public ThreadWithState(string text, int number)
    {
        boilerplate = text;
        value = number;
    }

    // The thread procedure performs the task, such as formatting 
    // and printing a document.
    public void ThreadProc()
    {
        Thread.Sleep(3000);
        Result = String.Format(boilerplate, value);
        Finished = true;
    }
}

// Entry point for the example.
//
public class Example
{
    private static System.Windows.Controls.TextBlock outputBlock;
    private static ThreadWithState tws;

    public static void Demo(System.Windows.Controls.TextBlock outputBlock)
    {
        Example.outputBlock = outputBlock;
        outputBlock.Text = "Click here to start the thread.\n";

        outputBlock.MouseLeftButtonUp += MouseUpStart;
    }

    private static void MouseUpStart(object sender, 
                                     System.Windows.Input.MouseButtonEventArgs e)
    {
        outputBlock.MouseLeftButtonUp -= MouseUpStart;

        // Supply the state information required by the task.
        tws = new ThreadWithState("This report displays the number {0}.", 42);

        // Create a thread to execute the task, and then
        // start the thread.
        Thread t = new Thread(tws.ThreadProc);
        t.Start();

        outputBlock.Text = "Click here to check for the result.\n";
        outputBlock.MouseLeftButtonUp += MouseUp;
    }

    private static void MouseUp(object sender, 
                                System.Windows.Input.MouseButtonEventArgs e)
    {

        if (tws.Finished)
        {
            outputBlock.MouseLeftButtonUp -= MouseUp;
            outputBlock.Text += tws.Result + "\n";
        }
        else
        {
            outputBlock.Text += "Not done yet.\n";
        }
    }
}

Retrieving Data with Callback Methods

The following example demonstrates a callback method that returns data from a thread. The constructor for the class that contains the data and the thread method also accepts a delegate representing the callback method; before the thread method ends, it invokes the callback delegate.

To run this example, see Building Examples That Use a Demo Method and a TextBlock Control.

Imports System.Threading

' The ThreadWithState class contains the information needed for
' a task, the method that executes the task, and a delegate
' to call when the task is complete.
'
Public Class ThreadWithState
    ' State information used in the task.
    Private boilerplate As String
    Private number As Integer
    Private callback As Action(Of String)

    ' The constructor obtains the state information and the
    ' callback delegate.
    Public Sub New(ByVal boilerplate As String, ByVal number As Integer, _
                   ByVal callback As Action(Of String))
        Me.boilerplate = boilerplate 
        Me.number = number
        Me.callback = callback
    End Sub

    ' The thread procedure performs the task, and then makes a cross-
    ' thread call to return the result.
    Public Sub ThreadProc()
        Thread.Sleep(3000)
        Dim result As String = String.Format(boilerplate, number) 
        If Not callback Is Nothing Then callback(result)
    End Sub

End Class

' Entry point for the example.
'
Public Class Example

    Private Shared outputBlock As System.Windows.Controls.TextBlock
    Private Shared tws As ThreadWithState

    Public Shared Sub Demo(ByVal outputBlock As System.Windows.Controls.TextBlock)

        Example.outputBlock = outputBlock

        ' Supply the state information required by the task.
        Dim tws As New ThreadWithState("This report displays the number {0}." & vbLf, _
                                       42, AddressOf ResultCallback)

        Dim t As New Thread(AddressOf tws.ThreadProc)
        t.Start()
        outputBlock.Text &= "Thread started." & vbLf

        AddHandler outputBlock.MouseLeftButtonUp, AddressOf MouseUp

    End Sub

    ' In order to update the TextBlock object, which is on the UI thread, 
    ' a cross-thread call must use the Dispatcher object that is associated 
    ' with the TextBlock. The DisplayOutput helper method and its delegate, 
    ' displayHelper, are used by the BeginInvoke method of the Dispatcher 
    ' object to append text to the TextBlock. 
    '
    Friend Shared Sub ResultCallback(ByVal msg As String)
        outputBlock.Dispatcher.BeginInvoke(displayHelper, msg)
    End Sub

    Private Shared displayHelper As New Action(Of String)(AddressOf DisplayOutput)
    Private Shared Sub DisplayOutput(ByVal msg As String)
        outputBlock.Text &= msg 
    End Sub

    ' Show that the user interface remains responsive.
    Private Shared Sub MouseUp(ByVal sender As Object, _
                               ByVal e As MouseButtonEventArgs)
        outputBlock.Text &= "Mouse Up." & vbLf
    End Sub

End Class
using System;
using System.Threading;

// The ThreadWithState class contains the information needed for
// a task, the method that executes the task, and a delegate
// to call when the task is complete.
//
public class ThreadWithState
{
    // State information used in the task.
    private string boilerplate;
    private int number;
    private Action<string> callback;

    // The constructor obtains the state information and the
    // callback delegate.
    public ThreadWithState(string boilerplate, int number, Action<string> callback)
    {
        this.boilerplate = boilerplate;
        this.number = number;
        this.callback = callback;
    }

    // The thread procedure performs the task, and then makes a cross-
    // thread call to return the result.
    public void ThreadProc()
    {
        Thread.Sleep(3000);
        string result = String.Format(boilerplate, number);
        if (callback != null) { callback(result); }
    }
}

// Entry point for the example.
//
public class Example
{
    private static System.Windows.Controls.TextBlock outputBlock;

    public static void Demo(System.Windows.Controls.TextBlock outputBlock)
    {
        Example.outputBlock = outputBlock;

        // Supply the state information required by the task.
        ThreadWithState tws = 
           new ThreadWithState("This report displays the number {0}.\n", 
                               42, ResultCallback);

        Thread t = new Thread(tws.ThreadProc);
        t.Start();
        outputBlock.Text += "Thread started.\n";

        outputBlock.MouseLeftButtonUp += MouseUp;
    }

    // In order to update the TextBlock object, which is on the UI thread, 
    // a cross-thread call must use the Dispatcher object that is associated 
    // with the TextBlock. The DisplayOutput helper method and its delegate, 
    // displayHelper, are used by the BeginInvoke method of the Dispatcher 
    // object to append text to the TextBlock. 
    //
    internal static void ResultCallback(string msg)
    {
        outputBlock.Dispatcher.BeginInvoke(displayHelper, msg);
    }

    private static Action<string> displayHelper = new Action<string>(DisplayOutput);
    private static void DisplayOutput(string msg)
    {
        outputBlock.Text += msg;
    }

    // Show that the user interface remains responsive.
    private static void MouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
    {
        outputBlock.Text += "Mouse Up.\n";
    }
}