Part 86 Multithreading in C#
What is a Process:
Process is what the operatin system uses to facilitate(帮助) the execution of a program by providing the resources required.Each process has unique process Id associated with(关联) it. You can view the process within which a program is being executed using windows task manager.
What is Thread:
Thread is a light weight(轻量级) process.A process has at least one thread which is commonly called(通常被称为) as main thread which actually executes the application code. A single process can hava multiple threads.
Please Note: All the threading related classes are present in(存在于) System.Threading namespace.
Part 87 Advantages and disadvantages of multithreading
Advantages of multithreading:
1, To maintain a responsive user interface(快速响应用户界面)
2, To make effcient use of processor time while waiting for I/O operations to complete.(适当的利用处理器,在等待I / O操作完成的这段时间。)
3, To split large, CPU-bound tasks to be processed simultaneously on a machine that has multiple processors/cores(分割大cpu密集型任务处理的机器上同时有多个处理器/核心)
Disadvantages of multithreading:
1, On a single processor/core machine threading can affect performance negatively as there is overhead involved with context-switching.(在单个处理器/核心的机器,线程会对上下文切换开销的性能有负面影响)
2, Have to write more lines of code to accomplish the same task.(需要编写更多的代码来完成相同的任务)
3,Multithreaded applications are difficult to write, understand, debug and maintain.(多线程应用程序很难写,理解、调试和维护。)
Please Note: Only use multithreading when the advantages of doing so outweigh the disavantages.
Part 88 ThreadStart delegate
To create a Thread, create an instance of Thread class and to it‘s constructor pass the name of the function that we want the thread to execute.
class Program { static void Main(string[] args) { Thread t = new Thread(Number.Print); t.Start(); } } public class Number { public static void Print() { for(int i=0;i<10;i++) { Console.WriteLine(i); } } }
Thread
Thread t = new Thread(Number.Print);
t.Start();
We can rewrite the above line using ThreadStart delegate as shown below
Thead t1 = new Thread(new ThreadStart(Number.Print));
t1.Start();
Why a delegate need to be passed as a parameter to the Thread class constructor?
The purpose of creating a Thread is to execute a function. A delegate is a type safe function pointer, meaning it points to a function that the thread has to execute. In short, all threads require an entry point to start execution. Any thread you create will need an explicitly defined entry point i.e(那就是说) a pointer to the function where they should begin execution. So threads always require a delegate.
In the code below, we are not explicitly creating the ThreadStart delegage, then how is it working here?
Thread t1 = new Thread(Number.Print);
t1.Start();
It‘s working in spite of(尽管) not creating the ThreadStart delegage explictly because the framework is doing it automatically for us.
Other ways, We can also rewrite the same line using delegate() keyword
Thread t = new Thread(delegate(){Number.Print();});
The same line rewritten using lambda expression
Thread t = new Thread(()=>Number.Print());
Part 89 ParameterizedThreadStart delegate
Use ParameterizedThreadStart delegate to pass data to the thread function
class Program { static void Main(string[] args) { Console.WriteLine("input a target number:"); object target = Console.ReadLine(); ParameterizedThreadStart pt = new ParameterizedThreadStart(Number.Print);//really need to write that?No,you can just pass the function into Thread class. Thread t = new Thread(pt);//new Thread(Number.Print); t.Start(target); Console.ReadLine(); } } public class Number { public static void Print(object target) { int number = 0; if (int.TryParse(target.ToString(), out number)) { for (int i = 0; i < number; i++) { Console.WriteLine(i); } } } }
How we are not explictly creating an instance of ParameterizedThreadStart delegate.Then how is it working?
It‘s working because, the compiler implicitly converts new Thread(Number.Print); to new Thread(new ParameterizedThreadStart(Number.Print));
When to use ParameterizedThreadStart over ThreadStart delegate?
Use ParameterizedThreadStart delegate if you have some data to pass to the Thread function, otherwise just use ThreadStart delegate.
Please note: Using parameterizedThreadStart delegate and Thread.Start(Object) method to pass data to the Thread function is not type safe as they operate on object datatype and any type of data can be passed.
If you try to change the data type of the target parameter of Print() function from object to int, a compiler error will be raised as the signature of Print() function does not match with the signature of ParameterizedThreadStart delegate.
Part 90 Passing data to the Thread function in a type safe manner(in a manner..在某种程度上)
To pass data to the Thread function in a type safe manner, encapsulate the thread function and the data it needs in a helper class and use the ThreadStart delegate to execute the thread function.
class Program { static void Main(string[] args) { Console.WriteLine("input a target number:"); int target = Convert.ToInt32(Console.ReadLine()); Number number = new Number(target); Thread t = new Thread(number.Print); t.Start(); Console.ReadLine(); } } public class Number { int _target; public Number(int target) { this._target = target; } public void Print() { int number = 0; for (int i = 0; i < _target; i++) { Console.WriteLine(i); } } }
Part 91 Retrieving data from Thread function using callback method
Callback method to get data from thread
Main thread retrieves(检索) the target number from the user.
Main thread creates a child thread and pass the target number to the child thread.
The child thread computes the sum of numbers and then returns the sum to the Main thread using callback function.
The callback method prints the sum of numbers.
Step 1: Create a callback delegate. The actual callback method signature should match with the signature of this delegate.
public delegate void SumOfNumberCallback(int sumOfNumber);
Step 2:Create a helper class to compute the sum of numbers and to call the callback method.
public class Number { int _target; SumOfNumberCallback _callBackMethod; public Number(int target, SumOfNumberCallback callBackMethod) { this._target = target; this._callBackMethod = callBackMethod; } public void PrintSumOfNumber() { int sum = 0; for (int i = 1; i <= _target; i++) { sum = sum + i; } if(_callBackMethod!=null) { _callBackMethod(sum); } } }
Step 3:This class consumes the Number class create in Step 2
class Program { public static void Main(string[] args) { Console.WriteLine("input a target number:"); int target = Convert.ToInt32(Console.ReadLine()); SumOfNumberCallback callBack = new SumOfNumberCallback(PrintSum); Number number = new Number(target,callBack); Thread t = new Thread(number.PrintSumOfNumber); t.Start(); Console.ReadLine(); } public static void PrintSum(int sum) { Console.WriteLine("sum of number =" + sum); } }
Part 92 Significance of Thread Join and Thread IsAlive functions
Thread.Join & Thread.IsAlive functions
Join blocks the current thread and makes it wait until the thread on which Join method is invoked completes.Join method also has a overload where we can specify the timeout. If we don‘t specify the timeout the calling thread waits indefinitely(无限期地), until the thread on which Join() is invoked completes. This overloaded Join(int millisecondesTimeout) method returns boolean. True if the thread has terminated(终止) otherwise false.
Join is particularly(特别地) useful when we need to wait and collect result from a thread execution or if we need to do some clean-up after the thread has completed.
IsAlive returns boolean. True if the thread is still executing otherwise false.
public static void Main(string[] args) { Console.WriteLine("main start"); Thread t1 = new Thread(ThreadFunction1); t1.Start(); Thread t2 = new Thread(ThreadFunction2); t2.Start(); if(t1.Join(1000)) { Console.WriteLine("threadfunction1 end"); } else { Console.WriteLine("threadfunction1 still working"); } if(t1.IsAlive) { for (int i = 1; i < 10; i++) { Console.WriteLine("threadfunction1 still working..."); Thread.Sleep(500); } } Console.WriteLine("main end"); } public static void ThreadFunction1() { Console.WriteLine("threadfunction1 start"); Thread.Sleep(5000); Console.WriteLine("threadfunction1 end"); } public static void ThreadFunction2() { Console.WriteLine("threadfunction2 start"); }
Part 93 Protecting shared resources from concurrent access in multithreading
Part 94 Difference between Monitor and lock in C#
Part 95 Deadlock in a multithreaded program
class Program { static void Main(string[] args) { Console.WriteLine("main start"); Account a1 = new Account(001,10000); Account a2 = new Account(002,20000); AccountManager m1 = new AccountManager(a1,a2,5000); Thread t1 = new Thread(m1.Transfer); t1.Name = "t1"; AccountManager m2 = new AccountManager(a2, a1, 3000); Thread t2 = new Thread(m2.Transfer); t2.Name = "t2"; t1.Start(); t2.Start(); t1.Join(); t2.Join(); Console.WriteLine("main end"); } } class Account { public int ID { get; set; } public double Balance { get; set; } public Account(int id, double balance) { this.ID = id; this.Balance = balance; } public void WithDraw(double amount) { Balance -= amount; } public void Deposit(double amount) { Balance += amount; } } class AccountManager { public Account FromAccount { get; set; } public Account ToAccount { get; set; } public double AmountToTransfer { get; set; } public AccountManager(Account from,Account to,double amountToTransfer) { this.FromAccount = from; this.ToAccount = to; this.AmountToTransfer = amountToTransfer; } public void Transfer() { Console.WriteLine(Thread.CurrentThread.Name+"try to acquire lock on"+FromAccount.ID.ToString()); lock (FromAccount) { Console.WriteLine(Thread.CurrentThread.Name+" acquired lock on "+FromAccount.ID.ToString()); Console.WriteLine(Thread.CurrentThread.Name+" suspended for 1 second"); Thread.Sleep(1000); Console.WriteLine(Thread.CurrentThread.Name+"back in action and try to acquire lock on" +ToAccount.ID.ToString()); lock (ToAccount) { Console.WriteLine("this code will not execute"); FromAccount.WithDraw(AmountToTransfer); ToAccount.Deposit(AmountToTransfer); } } } }
Part 96 How to resolve a deadlock in a multithreaded program
static void Main(string[] args) { Console.WriteLine("main start"); Account a1 = new Account(101,10000); Account a2 = new Account(102,20000); AccountManager m1 = new AccountManager(a1,a2,5000); Thread t1 = new Thread(m1.Transfer); t1.Name = "t1"; AccountManager m2 = new AccountManager(a2, a1, 3000); Thread t2 = new Thread(m2.Transfer); t2.Name = "t2"; t1.Start(); t2.Start(); t1.Join(); t2.Join(); Console.WriteLine("main end"); } } class Account { public int ID { get; set; } public double Balance { get; set; } public Account(int id, double balance) { this.ID = id; this.Balance = balance; } public void WithDraw(double amount) { Balance -= amount; } public void Deposit(double amount) { Balance += amount; } } class AccountManager { public Account FromAccount { get; set; } public Account ToAccount { get; set; } public double AmountToTransfer { get; set; } public AccountManager(Account from,Account to,double amountToTransfer) { this.FromAccount = from; this.ToAccount = to; this.AmountToTransfer = amountToTransfer; } public void Transfer() { object _lock1, _lock2; if(FromAccount.ID<ToAccount.ID) { _lock1 = FromAccount; _lock2 = ToAccount; } else { _lock1 = ToAccount; _lock2 = FromAccount; } Console.WriteLine(Thread.CurrentThread.Name+"try to acquire lock on "+((Account)_lock1).ID.ToString()); lock (_lock1) { Console.WriteLine(Thread.CurrentThread.Name + " acquired lock on " + ((Account)_lock1).ID.ToString()); Console.WriteLine(Thread.CurrentThread.Name+" suspended for 1 second"); Thread.Sleep(1000); Console.WriteLine(Thread.CurrentThread.Name + "back in action and try to acquire lock on " + ((Account)_lock2).ID.ToString()); lock (_lock2) { Console.WriteLine(Thread.CurrentThread.Name + " acquired lock on " + ((Account)_lock2).ID.ToString()); FromAccount.WithDraw(AmountToTransfer); ToAccount.Deposit(AmountToTransfer); Console.WriteLine(Thread.CurrentThread.Name+" Transferd "+AmountToTransfer.ToString()+" from "+FromAccount.ID.ToString()+" to "+ToAccount.ID.ToString()); } } }
Part 97 Performance of a multithreaded program
class Program { static void Main(string[] args) { Stopwatch s = new Stopwatch(); s.Start(); EvenNumbersSum(); OddNumbersSum(); s.Stop(); Console.WriteLine("before using multiple threads"+s.ElapsedMilliseconds); s = new Stopwatch(); s.Start(); Thread t1 = new Thread(EvenNumbersSum); Thread t2 = new Thread(OddNumbersSum); t1.Start(); t2.Start(); t1.Join(); t2.Join(); s.Stop(); Console.WriteLine("after using multiple threads"+s.ElapsedMilliseconds); } public static void EvenNumbersSum() { double sum=0; for(int i=0;i<=50000000;i++) { if(i%2==0) { sum += i; } } Console.WriteLine("sum= "+sum); } public static void OddNumbersSum() { double sum = 0; for (int i = 0; i <= 50000000; i++) { if (i % 2 == 1) { sum += i; } } Console.WriteLine("sum= " + sum); } }