Example analysis of three ways of transferring parameters to multithread by C

  • 2021-08-28 20:54:22
  • OfStack

In this paper, three ways of C # to transmit parameters to multithreads are described by examples. Share it for your reference, as follows:

From "C # Advanced Programming", we know that there are two ways to pass parameters to threads, one is to use Thread constructor with ParameterizedThreadStart delegate parameters, and the other is to create a custom class and define the methods of threads as the methods of instances, so that the data of instances can be initialized, and then the threads can be started.

Mode 1: Use ParameterizedThreadStart delegate

If an ParameterizedThreadStart delegate is used, the entry of the thread must have a parameter of type object and a return type of void. Let's look at the following example:


using System;
using System.Threading;
namespace ThreadWithParameters
{
  class Program
  {
    static void Main(string[] args)
    {
      string hello = "hello world";
      // It can also be abbreviated here as Thread thread = new Thread(ThreadMainWithParameters);
      // But in order to let everyone know that what is used here is ParameterizedThreadStart Commissioned, there is no abbreviation 
      Thread thread = new Thread(new ParameterizedThreadStart(ThreadMainWithParameters));
      thread.Start(hello);
      Console.Read();
    }
    static void ThreadMainWithParameters(object obj)
    {
      string str = obj as string;
      if(!string.IsNullOrEmpty(str))
        Console.WriteLine("Running in a thread,received: {0}", str);
    }
  }
}

A little bit of trouble here is that the parameters in the ThreadMainWithParameters method must be of type object, so we need to type conversion. Just look at the declaration of the ParameterizedThreadStart delegate to see why the parameter must be of type object.

public delegate void ParameterizedThreadStart (object obj); //ParameterizedThreadStart Declaration of Delegation

Way 2: Create a custom class

Define a class, in which the definition of the required fields, the main method of the thread is defined as a class of instance method, said is not very clear, or see the actual example.


using System;
using System.Threading;
namespace ThreadWithParameters
{
  public class MyThread
  {
    private string data;
    public MyThread(string data)
    {
      this.data = data;
    }
    public void ThreadMain()
    {
      Console.WriteLine("Running in a thread,data: {0}", data);
    }
  }
  class Program
  {
    static void Main(string[] args)
    {
      MyThread myThread = new MyThread("hello world");
      Thread thread = new Thread(myThread.ThreadMain);
      thread.Start();
      Console.Read();
    }
  }
}

I am not very satisfied with this method, so I can't create a new class if I encounter a time-consuming method. . .

So what better way is not to force type conversion or create a new class?

The following is an introduction to a method I found inadvertently. I don't remember where I saw it. Sin. .

Mode 3: Use anonymous methods


using System;
using System.Threading;
namespace ThreadWithParameters
{
  class Program
  {
    static void Main(string[] args)
    {
      string hello = "hello world";
      // If it is written as Thread thread = new Thread(ThreadMainWithParameters(hello)); In this form, an error will be reported at compile time 
      Thread thread = new Thread(() => ThreadMainWithParameters(hello));
      thread.Start();
      Console.Read();
    }
    static void ThreadMainWithParameters(string str)
    {
       Console.WriteLine("Running in a thread,received: {0}", str);
    }
  }
}

Wow, you will find that it runs successfully without casting or creating a new class.

But why can this method work? According to the prompt of @ Flurry Spring and Autumn yesterday, I also decompiled it with ildasm. Indeed, as he said, my so-called third method is actually the same as the second method, but the custom class compiler did it for us.

The following is the IL code decompiled by the main method in the third way:


.method private hidebysig static void Main(string[] args) cil managed
{
  .entrypoint
  //  Code size     51 (0x33)
  .maxstack 3
  .locals init ([0] class [mscorlib]System.Threading.Thread thread,
       [1] class ThreadWithParameters.Program/'<>c__DisplayClass1' 'CS$<>8__locals2')
  IL_0000: newobj   instance void ThreadWithParameters.Program/'<>c__DisplayClass1'::.ctor()
  IL_0005: stloc.1
  IL_0006: nop
  IL_0007: ldloc.1
  IL_0008: ldstr   "hello world"
  IL_000d: stfld   string ThreadWithParameters.Program/'<>c__DisplayClass1'::hello
  IL_0012: ldloc.1
  IL_0013: ldftn   instance void ThreadWithParameters.Program/'<>c__DisplayClass1'::'<Main>b__0'()
  IL_0019: newobj   instance void [mscorlib]System.Threading.ThreadStart::.ctor(object, native int)
  IL_001e: newobj   instance void [mscorlib]System.Threading.Thread::.ctor(class [mscorlib]System.Threading.ThreadStart)
  IL_0023: stloc.0
  IL_0024: ldloc.0
  IL_0025: callvirt  instance void [mscorlib]System.Threading.Thread::Start()
  IL_002a: nop
  IL_002b: call    int32 [mscorlib]System.Console::Read()
  IL_0030: pop
  IL_0031: nop
  IL_0032: ret
} // end of method Program::Main

Look at the IL code for Mode 2:


.method private hidebysig static void Main(string[] args) cil managed
{
  .entrypoint
  //  Code size     44 (0x2c)
  .maxstack 3
  .locals init ([0] class ThreadWithParameters.MyThread myThread,
       [1] class [mscorlib]System.Threading.Thread thread)
  IL_0000: nop
  IL_0001: ldstr   "hello world"
  IL_0006: newobj   instance void ThreadWithParameters.MyThread::.ctor(string)
  IL_000b: stloc.0
  IL_000c: ldloc.0
  IL_000d: ldftn   instance void ThreadWithParameters.MyThread::ThreadMain()
  IL_0013: newobj   instance void [mscorlib]System.Threading.ThreadStart::.ctor(object, native int)
  IL_0018: newobj   instance void [mscorlib]System.Threading.Thread::.ctor(class [mscorlib]System.Threading.ThreadStart)
  IL_001d: stloc.1
  IL_001e: ldloc.1
  IL_001f: callvirt  instance void [mscorlib]System.Threading.Thread::Start()
  IL_0024: nop
  IL_0025: call    int32 [mscorlib]System.Console::Read()
  IL_002a: pop
  IL_002b: ret
} // end of method Program::Main

Comparing the codes at both ends, we can find that both of them have an newobj, which is used to initialize an instance of a class. In the third way, the compiler generates a class: c__DisplayClass1


IL_0000: newobj   instance void ThreadWithParameters.Program/'<>c__DisplayClass1'::.ctor()
IL_0006: newobj   instance void ThreadWithParameters.MyThread::.ctor(string)

Note: Simplicity is not necessarily a good thing. Anonymous methods can easily cause imperceptible errors

I hope this article is helpful to everyone's C # programming.


Related articles: