static void Ex12() { //same Bank that //can Transfer an amount from a source BankAccount //to a target BankAccount. //if multiple threads try to transfer from the same //accounts, concurrency problems could arise.... ExampleLibrary.BankScenario.BankAccount ba1 = new ExampleLibrary.BankScenario.BankAccount() { Id = 1 }; ExampleLibrary.BankScenario.BankAccount ba2 = new ExampleLibrary.BankScenario.BankAccount() { Id = 2 }; ba1.Deposit(100); ExampleLibrary.BankScenario.NotSynchronized.Bank bank = new ExampleLibrary.BankScenario.NotSynchronized.Bank(); for (int i = 0; i < 100; i++) //200 concurrent Threads running transfer { new Thread(() => { bank.Transfer(ba1, ba2, 50); Console.WriteLine($"{ba1.Id} == {ba1.Saldo}\t{ba2.Id} == {ba2.Saldo}"); }).Start(); new Thread(() => { bank.Transfer(ba2, ba1, 50); Console.WriteLine($"{ba1.Id} == {ba1.Saldo}\t{ba2.Id} == {ba2.Saldo}"); }).Start(); } //we could see weird numbers popping up.... }
static void Ex11() { //in our scenario we have a Bank that //can Transfer an amount from a source BankAccount //to a target BankAccount. //if we do not use multithreading, //no problems will arise ExampleLibrary.BankScenario.BankAccount ba1 = new ExampleLibrary.BankScenario.BankAccount() { Id = 1 }; ExampleLibrary.BankScenario.BankAccount ba2 = new ExampleLibrary.BankScenario.BankAccount() { Id = 2 }; ba1.Deposit(100); ExampleLibrary.BankScenario.NotSynchronized.Bank bank = new ExampleLibrary.BankScenario.NotSynchronized.Bank(); for (int i = 0; i < 100; i++) //200 transfer { bank.Transfer(ba1, ba2, 50); bank.Transfer(ba2, ba1, 50); } Console.WriteLine($"{ba1.Id} == {ba1.Saldo}\t{ba2.Id} == {ba2.Saldo}"); //ba1.Saldo == 100, ba2.Saldo == 0 }
static void Ex15() { //this Bank has an Event it's waiting for //in order to proceed with the Transfer. ExampleLibrary.BankScenario.BankAccount ba1 = new ExampleLibrary.BankScenario.BankAccount() { Id = 1 }; ExampleLibrary.BankScenario.BankAccount ba2 = new ExampleLibrary.BankScenario.BankAccount() { Id = 2 }; ba1.Deposit(100); ExampleLibrary.BankScenario.EventWaitHandles.Bank bank = new ExampleLibrary.BankScenario.EventWaitHandles.Bank(); for (int i = 0; i < 100; i++) //200 concurrent Threads running transfer { new Thread(() => { bank.Transfer(ba1, ba2, 50); Console.WriteLine($"{ba1.Id} == {ba1.Saldo}\t{ba2.Id} == {ba2.Saldo}"); }).Start(); new Thread(() => { bank.Transfer(ba2, ba1, 50); Console.WriteLine($"{ba1.Id} == {ba1.Saldo}\t{ba2.Id} == {ba2.Saldo}"); }).Start(); } }
static void Ex13() { //this Bank locks the bankAccounts it wants to use //when Transfering an amount from a source BankAccount //to a target BankAccount. //if multiple threads try to transfer from the same //accounts, deadlock problems could arise.... ExampleLibrary.BankScenario.BankAccount ba1 = new ExampleLibrary.BankScenario.BankAccount() { Id = 1 }; ExampleLibrary.BankScenario.BankAccount ba2 = new ExampleLibrary.BankScenario.BankAccount() { Id = 2 }; ba1.Deposit(100); ExampleLibrary.BankScenario.Deadlocked.Bank bank = new ExampleLibrary.BankScenario.Deadlocked.Bank(); for (int i = 0; i < 100; i++) //200 concurrent Threads running transfer { new Thread(() => { bank.Transfer(ba1, ba2, 50); Console.WriteLine($"{ba1.Id} == {ba1.Saldo}\t{ba2.Id} == {ba2.Saldo}"); }).Start(); new Thread(() => { bank.Transfer(ba2, ba1, 50); Console.WriteLine($"{ba1.Id} == {ba1.Saldo}\t{ba2.Id} == {ba2.Saldo}"); }).Start(); } //we could never get here.... }
static void Ex03() { //This example is equivalent to Ex15 of the Thread examples, //but it's faster because it does not spawn 200 new Threads //and it uses the ThreadPool instead. ExampleLibrary.BankScenario.BankAccount ba1 = new ExampleLibrary.BankScenario.BankAccount() { Id = 1 }; ExampleLibrary.BankScenario.BankAccount ba2 = new ExampleLibrary.BankScenario.BankAccount() { Id = 2 }; ba1.Deposit(100); ExampleLibrary.BankScenario.EventWaitHandles.Bank bank = new ExampleLibrary.BankScenario.EventWaitHandles.Bank(); for (int i = 0; i < 100; i++) //200 concurrent Threads running transfer { ThreadPool.QueueUserWorkItem(_ => { bank.Transfer(ba1, ba2, 50); Console.WriteLine($"{ba1.Id} == {ba1.Saldo}\t{ba2.Id} == {ba2.Saldo}"); }); ThreadPool.QueueUserWorkItem(_ => { bank.Transfer(ba2, ba1, 50); Console.WriteLine($"{ba1.Id} == {ba1.Saldo}\t{ba2.Id} == {ba2.Saldo}"); }); } }
static void Ex17() { //In this scenario we want to wait for all Thread to finish //before we continue. //Another thing we could do is to create just one event //and set it when a counter reaches 200. //BUT we have to increase the same counter on 200 different threads, //meaning that we have to do it in a thread safe manner. AutoResetEvent doneEvent = new AutoResetEvent(false); ExampleLibrary.BankScenario.BankAccount ba1 = new ExampleLibrary.BankScenario.BankAccount() { Id = 1 }; ExampleLibrary.BankScenario.BankAccount ba2 = new ExampleLibrary.BankScenario.BankAccount() { Id = 2 }; ba1.Deposit(100); ExampleLibrary.BankScenario.EventWaitHandles.Bank bank = new ExampleLibrary.BankScenario.EventWaitHandles.Bank(); int counter = 0; for (int i = 0; i < 100; i++) //200 concurrent Threads running transfer { new Thread(() => { bank.Transfer(ba1, ba2, 50); Interlocked.Increment(ref counter); Console.WriteLine($"{ba1.Id} == {ba1.Saldo}\t{ba2.Id} == {ba2.Saldo}"); if (counter == 200) { doneEvent.Set(); } }).Start(); new Thread(() => { bank.Transfer(ba2, ba1, 50); Interlocked.Increment(ref counter); Console.WriteLine($"{ba1.Id} == {ba1.Saldo}\t{ba2.Id} == {ba2.Saldo}"); if (counter == 200) { doneEvent.Set(); } }).Start(); } doneEvent.WaitOne(); Console.WriteLine("DONE!!"); }
static void Ex14() { //this Bank tries to lock the bankAccounts it wants to use //for 10 milliseconds, after which it stops trying. //that's why this transfer could fail. //if multiple threads try to transfer from the same //accounts, the transfer could fail, //so we can decide if we want to keep trying, //or stop, or who knows what ExampleLibrary.BankScenario.BankAccount ba1 = new ExampleLibrary.BankScenario.BankAccount() { Id = 1 }; ExampleLibrary.BankScenario.BankAccount ba2 = new ExampleLibrary.BankScenario.BankAccount() { Id = 2 }; ba1.Deposit(100); ExampleLibrary.BankScenario.Timeout.Bank bank = new ExampleLibrary.BankScenario.Timeout.Bank(); for (int i = 0; i < 100; i++) //200 concurrent Threads running transfer { new Thread(() => { while (!bank.Transfer(ba1, ba2, 50)) //it could timeout { Console.Write("."); } //keep trying until we get it Console.WriteLine($"{ba1.Id} == {ba1.Saldo}\t{ba2.Id} == {ba2.Saldo}"); }).Start(); new Thread(() => { while (!bank.Transfer(ba2, ba1, 50)) //it could timeout { Console.Write("."); } //keep trying until we get it Console.WriteLine($"{ba1.Id} == {ba1.Saldo}\t{ba2.Id} == {ba2.Saldo}"); }).Start(); } }
static void Ex16() { //In this scenario we want to wait for all Thread to finish //before we continue. //We could create 200 AutoResetEvents, put them in an Array, //signal each one of them from a Thread //and use EventWaitHandle.WaitAll, //but they would be too many. AutoResetEvent[] events = Enumerable.Repeat(new AutoResetEvent(false), 200).ToArray(); ExampleLibrary.BankScenario.BankAccount ba1 = new ExampleLibrary.BankScenario.BankAccount() { Id = 1 }; ExampleLibrary.BankScenario.BankAccount ba2 = new ExampleLibrary.BankScenario.BankAccount() { Id = 2 }; ba1.Deposit(100); ExampleLibrary.BankScenario.EventWaitHandles.Bank bank = new ExampleLibrary.BankScenario.EventWaitHandles.Bank(); for (int i = 0; i < 100; i++) //200 concurrent Threads running transfer { new Thread(() => { bank.Transfer(ba1, ba2, 50); events[i].Set(); Console.WriteLine($"{ba1.Id} == {ba1.Saldo}\t{ba2.Id} == {ba2.Saldo}"); }).Start(); new Thread(() => { bank.Transfer(ba2, ba1, 50); events[i + 100].Set(); Console.WriteLine($"{ba1.Id} == {ba1.Saldo}\t{ba2.Id} == {ba2.Saldo}"); }).Start(); } //boom! WaitHandle.WaitAll(events); Console.WriteLine("DONE!!"); }
static void Ex18() { //In this scenario we want to wait for all Thread to finish //before we continue. //Another thing we could do is to use a CountdownEvent. CountdownEvent doneEvent = new CountdownEvent(200); ExampleLibrary.BankScenario.BankAccount ba1 = new ExampleLibrary.BankScenario.BankAccount() { Id = 1 }; ExampleLibrary.BankScenario.BankAccount ba2 = new ExampleLibrary.BankScenario.BankAccount() { Id = 2 }; ba1.Deposit(100); ExampleLibrary.BankScenario.EventWaitHandles.Bank bank = new ExampleLibrary.BankScenario.EventWaitHandles.Bank(); for (int i = 0; i < 100; i++) //200 concurrent Threads running transfer { new Thread(() => { bank.Transfer(ba1, ba2, 50); Console.WriteLine($"{ba1.Id} == {ba1.Saldo}\t{ba2.Id} == {ba2.Saldo}"); doneEvent.Signal(); }).Start(); new Thread(() => { bank.Transfer(ba2, ba1, 50); Console.WriteLine($"{ba1.Id} == {ba1.Saldo}\t{ba2.Id} == {ba2.Saldo}"); doneEvent.Signal(); }).Start(); } doneEvent.Wait(); //waiting for it to reach 0 Console.WriteLine("DONE!!"); }