501 likes | 687 Views
Asynchronous Programming in C# 5.0. Muzaffer DOĞAN Anadolu University Computer Engineering Dept. My Research Area. Signal Processing Image Processing Wavelets. Outline. Traditional multi-threaded programming The problems in threads C#’s solution An example Conclusion.
E N D
Asynchronous Programming in C# 5.0 Muzaffer DOĞAN Anadolu University Computer Engineering Dept.
My Research Area • Signal Processing • Image Processing • Wavelets Muzaffer Doğan - Comp. Eng. Dept., Anadolu University, Eskişehir, Turkey
Outline • Traditional multi-threaded programming • The problems in threads • C#’s solution • An example • Conclusion Muzaffer Doğan - Comp. Eng. Dept., Anadolu University, Eskişehir, Turkey
Why Parallel Processing? • Responsive user interface • Allowing multiple user requests • ASP.NET, web services, etc. • Parallel programming Muzaffer Doğan - Comp. Eng. Dept., Anadolu University, Eskişehir, Turkey
Threading • Thread: Execution path that can proceed independently of others • Single-threaded programs: Just one thread runs in the process • Multithreaded programs: Multiple thread run in the process • All threads share the same execution environment (memory) Muzaffer Doğan - Comp. Eng. Dept., Anadolu University, Eskişehir, Turkey
Using Threads in C# • Import the namespace System.Threading • Create a Threadobject using a ThreadStartdelegate • Call the thread by its Startmethod • If you want to wait until the end of the thread, use its Joinmethod • If you want to delay a thread, use the static Thread.Sleepmethod Muzaffer Doğan - Comp. Eng. Dept., Anadolu University, Eskişehir, Turkey
using System; using System.Threading; class ThreadTest { static void Main() { Thread t = new Thread(WriteY); // Kick off a new thread t.Start(); // running WriteY() // Simultaneously, do something on the main thread. for (inti = 0; i < 1000; i++) Console.Write("x"); } static void WriteY() { for (inti = 0; i < 1000; i++) Console.Write("y"); } } Muzaffer Doğan - Comp. Eng. Dept., Anadolu University, Eskişehir, Turkey
The Output xxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxyyyyyyyyyyyyy yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy yyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ... Muzaffer Doğan - Comp. Eng. Dept., Anadolu University, Eskişehir, Turkey
The Mechanism Muzaffer Doğan - Comp. Eng. Dept., Anadolu University, Eskişehir, Turkey
Local Variables staticvoid Main() { new Thread (Go).Start(); // Call Go() on a new thread Go(); // Call Go() on the main thread } staticvoidGo() { // Declare and use a local variable - 'cycles' for (int cycles = 0; cycles < 5; cycles++) Console.Write('?'); } Muzaffer Doğan - Comp. Eng. Dept., Anadolu University, Eskişehir, Turkey
The Output ?????????? Muzaffer Doğan - Comp. Eng. Dept., Anadolu University, Eskişehir, Turkey
Shared Variables classThreadTest{ static bool_done; // Shared between all threads staticvoid Main(){ newThread (Go).Start(); Go(); } staticvoidGo(){ if (!_done) { _done = true; Console.WriteLine ("Done"); } } } Muzaffer Doğan - Comp. Eng. Dept., Anadolu University, Eskişehir, Turkey
The Output • The output will be a single Done text printed on the screen • The main thread changes the value of _done variable first • The new thread gets the changed value of _done and prints nothing Muzaffer Doğan - Comp. Eng. Dept., Anadolu University, Eskişehir, Turkey
A small change… classThreadTest{ static bool_done; // Shared between all threads staticvoid Main(){ newThread (Go).Start(); Go(); } staticvoidGo(){ if (!_done) { Console.WriteLine ("Done"); _done = true;} } } Muzaffer Doğan - Comp. Eng. Dept., Anadolu University, Eskişehir, Turkey
The Output • Probability of printing two Done texts increases because the Console.WriteLine method may take a long time • The main thread may get the _done variable first as false • While the main thread prints the text Done on the screen, the second thread may take the _done variable with the value false • In this case, twoDone texts are printed • If the main thread changes the value of _done before the second thread reads its value, only oneDone is printed on the screen • This example introduces the Thread Safety (or lack of it) Muzaffer Doğan - Comp. Eng. Dept., Anadolu University, Eskişehir, Turkey
Thread Safety • If two threads read or write a memory space concurrently without any errors, it’s called thread safety • Safety of the .NET Framework methods are documented in their help pages • Locking may be a solution for the thread safety Muzaffer Doğan - Comp. Eng. Dept., Anadolu University, Eskişehir, Turkey
classThreadSafe{ staticbool _done; static readonly object _locker = new object(); staticvoid Main(){ newThread (Go).Start(); Go(); } staticvoidGo(){ lock(_locker){ if (!_done) { Console.WriteLine ("Done"); _done = true; } } } } Muzaffer Doğan - Comp. Eng. Dept., Anadolu University, Eskişehir, Turkey
The Output Done Done Muzaffer Doğan - Comp. Eng. Dept., Anadolu University, Eskişehir, Turkey
Passing Data to a Thread • The easiest way is using lambda expressions • Lambda expressions can be thought as internally written functions • Lambda expressions didn’t exist prior to C# 3.0 • Any number of data can be passed • The second way is to use the Thread’s Start method • Only one data can be passed as an object type • Type casting is required in the function • More than one data can be passed in a struct element Muzaffer Doğan - Comp. Eng. Dept., Anadolu University, Eskişehir, Turkey
1) Using Lambda Expression staticvoid Main() { Thread t = newThread ( () => Print ("Hellofrom t!") ); t.Start(); } staticvoidPrint (stringmessage) { Console.WriteLine (message); } Muzaffer Doğan - Comp. Eng. Dept., Anadolu University, Eskişehir, Turkey
2) Using Thread’s Start Method staticvoid Main(){ Threadt = newThread (Print); t.Start("Hellofrom t!"); } staticvoidPrint (objectmessageObj){ stringmessage = (string)messageObj; Console.WriteLine(message); } Muzaffer Doğan - Comp. Eng. Dept., Anadolu University, Eskişehir, Turkey
Captured Variables for (int i = 0; i < 10; i++) { newThread (() => Console.Write (i)).Start(); } • The output is nondeterministic! Here’s a typical result: • 0223557799 Muzaffer Doğan - Comp. Eng. Dept., Anadolu University, Eskişehir, Turkey
Solution • Using a temporary variable solves the problem: for (int i = 0; i < 10; i++) { inttemp = i; new Thread (() => Console.Write (temp)).Start(); } • One of the possible outputs: 0143256789 Muzaffer Doğan - Comp. Eng. Dept., Anadolu University, Eskişehir, Turkey
Exception Handling • If an exception occurs in a thread, it can’t be detected in the main block: publicstaticvoid Main(){ try{ newThread (Go).Start(); } catch(Exceptionex){ // We'llneverget here! Console.WriteLine("Exception!"); } } staticvoidGo() { thrownull; } // NullReferenceException Muzaffer Doğan - Comp. Eng. Dept., Anadolu University, Eskişehir, Turkey
Solution • Move the exception handling into the thread function • Difficult to code! • If exception is not handled, the program crushes! • Handling exceptions at higher levels would be better! Muzaffer Doğan - Comp. Eng. Dept., Anadolu University, Eskişehir, Turkey
Signaling • In a Windows Forms application, all events are controlled by the main thread, namely, UI thread • If a long operation works, the UI becomes unresponsive • You can execute the long operation in a separate thread • At the end of the worker thread, you need to send a signal to the UI thread so that it displays the result • The UI thread can be signaled with the methods BeginInvokeand Invoke • The difference is that the Invoke method blocks the worker thread until the invoked method in the UI thread ends Muzaffer Doğan - Comp. Eng. Dept., Anadolu University, Eskişehir, Turkey
Unresponsive UI private void Button1_Click(object sender, EventArgs e) { this.Text = "A long operation has started..."; DoWork(); this.Text = "Completed!"; } privatevoidDoWork() { Thread.Sleep(3000);// Unresponsive UI! } Muzaffer Doğan - Comp. Eng. Dept., Anadolu University, Eskişehir, Turkey
Make it responsive! private void Button1_Click(object sender, EventArgs e) { this.Text = "A long operation has started..."; Thread t = new Thread(DoWork); t.Start(); } privatevoidDoWork() { Thread.Sleep(3000); this.Text = "Completed!"; } Muzaffer Doğan - Comp. Eng. Dept., Anadolu University, Eskişehir, Turkey
Signaling UI Thread private void Button1_Click(object sender, EventArgs e){ this.Text= "A long operation has started..."; newThread(DoWork).Start(); } privatevoidDoWork(){ Thread.Sleep(3000); BeginInvoke(newEventHandler(Completed)); } private void Completed(object sender, EventArgs e) { this.Text = "Completed!"; } Muzaffer Doğan - Comp. Eng. Dept., Anadolu University, Eskişehir, Turkey
Signaling by a Lambda Expression private void Button1_Click(object sender, EventArgs e){ this.Text= "A long operation has started..."; newThread(DoWork).Start(); } private void DoWork() { Thread.Sleep(3000); Action action= () => this.Text = "Completed!"; BeginInvoke(action); } Muzaffer Doğan - Comp. Eng. Dept., Anadolu University, Eskişehir, Turkey
Summary of Problems • Accessing a shared variable • Passing data to a thread • Exception handling • Signaling (or continuation) Muzaffer Doğan - Comp. Eng. Dept., Anadolu University, Eskişehir, Turkey
Solution • Asynchronous programming! • Write your methods synchronously • Replace synchronous method calls with asynchronous method calls, and await them. • Upgrade your asynchronous methods’ return types to Task or Task<TResult> so that they’re awaitable. Muzaffer Doğan - Comp. Eng. Dept., Anadolu University, Eskişehir, Turkey
private asyncvoid Button1_Click(object sender, EventArgs e) { this.Text = "A long operation has started..."; awaitDoWork(); this.Text = "Completed!"; } privateTaskDoWork() { return Task.Run(() => Thread.Sleep(3000)); } Muzaffer Doğan - Comp. Eng. Dept., Anadolu University, Eskişehir, Turkey
private void asyncButton1_Click(object sender, EventArgs e) { this.Text = "A long operation has started..."; awaitDoWork(); this.Text = "Completed!"; } privateasyncTaskDoWork() { awaitTask.Delay(3000); } Muzaffer Doğan - Comp. Eng. Dept., Anadolu University, Eskişehir, Turkey
private void asyncButton1_Click(object sender, EventArgs e) { this.Text = "A long operation has started..."; awaitTask.Delay(3000); this.Text = "Completed!"; } Continuation problem is automatically solved! Muzaffer Doğan - Comp. Eng. Dept., Anadolu University, Eskişehir, Turkey
Lambda Expressions can also be used private void asyncButton1_Click(object sender, EventArgs e) { this.Text = "A long operation has started..."; awaitTask.Run(() => Thread.Sleep(3000)); this.Text = "Completed!"; } Muzaffer Doğan - Comp. Eng. Dept., Anadolu University, Eskişehir, Turkey
Passing Data – Synchronous Version staticvoid Main(string[] args) { for (int i = 0; i < 10; i++) { Print(i); } } staticvoidPrint(intnum) { Console.Write(num); } Muzaffer Doğan - Comp. Eng. Dept., Anadolu University, Eskişehir, Turkey
Passing Data – Asynchronous Version staticvoid Main(string[] args){ for (int i = 0; i < 10; i++) { Print(i); } Thread.Sleep(3000); // Wait until all threads finish } staticasyncvoidPrint(intnum){ awaitTask.Run(() => Console.Write(num)); } // One of the possible outputs: 0143256789 Muzaffer Doğan - Comp. Eng. Dept., Anadolu University, Eskişehir, Turkey
Exception Handling private async void Button1_Click(object sender, EventArgs e) { try { awaitDoWork(); } catch (Exception ex) { MessageBox.Show(ex.Message); } } private Task DoWork() { return Task.Run(() => { Thread.Sleep(3000); throw new NullReferenceException(); }); } Muzaffer Doğan - Comp. Eng. Dept., Anadolu University, Eskişehir, Turkey
Summing up Array Elements • Let’s create an array and compute the sum of its elements by dividing the array into two • Each part will be summed by a separate thread Muzaffer Doğan - Comp. Eng. Dept., Anadolu University, Eskişehir, Turkey
Synchronous Solution private int[] A = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; privateintSum(int start, intlength){ intsum = 0; for (int i = start; i < start + length; i++){ sum+= A[i]; } returnsum; } Muzaffer Doğan - Comp. Eng. Dept., Anadolu University, Eskişehir, Turkey
Synchronous Solution private void Button1_Click(object sender, EventArgs e) { int sum1 = Sum(0, 5); intsum2 = Sum(5, 5); int sum = sum1 + sum2; MessageBox.Show(sum.ToString()); } Muzaffer Doğan - Comp. Eng. Dept., Anadolu University, Eskişehir, Turkey
Asynchronous Solution privateTask<int>Sum(int start, intlength){ returnTask.Run(() => { intsum = 0; for (int i = start; i < start + length; i++) { sum += A[i]; } returnsum; }); } Muzaffer Doğan - Comp. Eng. Dept., Anadolu University, Eskişehir, Turkey
Asynchronous Solution private async void Button1_Click(object sender, EventArgs e) { int sum1 = await Sum(0, 5); int sum2 = await Sum(5, 5); int sum = sum1 + sum2; MessageBox.Show(sum.ToString()); } Function calls are not parallel! Muzaffer Doğan - Comp. Eng. Dept., Anadolu University, Eskişehir, Turkey
Solution: WhenAll private asyncvoid Button1_Click(object sender, EventArgs e) { int[] sums = await Task.WhenAll( Sum(0, 5), Sum(5, 5)); intsum = sums[0] + sums[1]; MessageBox.Show(sum.ToString()); } Muzaffer Doğan - Comp. Eng. Dept., Anadolu University, Eskişehir, Turkey
WhenAny private asyncvoid Button1_Click(object sender, EventArgs e) { Task<int> winner = await Task.WhenAny( Sum(0, 5), Sum(5, 5)); int sum = winner.Result; MessageBox.Show(sum.ToString()); } Muzaffer Doğan - Comp. Eng. Dept., Anadolu University, Eskişehir, Turkey
Conclusion • Asynchoronous Programming support of C# • simplifies continuation • simplifies exception handling • simplifies multi-threaded programming • simplifies thread-safety • simplifies passing data to a thread • introduces awaitablefunctions • but requires some experience Muzaffer Doğan - Comp. Eng. Dept., Anadolu University, Eskişehir, Turkey
Thank you and questions… Muzaffer Doğan - Comp. Eng. Dept., Anadolu University, Eskişehir, Turkey