Введение в разработку многопоточных приложений

682 views

Published on

Открытый семинар для студентов в компании CUSTIS (31 октября 2012 года).

Лектор: Дмитрий Костиков, ведущий разработчик C#, сертифицированный специалист Microsoft (MCTS), специалист по разработке корпоративных приложений (С#, SQL, Win/Web).

Аннотация: На семинаре будут рассмотрены принципы работы с многопоточностью, эволюция представлений и методик, описаны проблемы, возникающие при разработке многопоточных приложений, а также механизмы для работы с многопоточностью, применяющиеся в Windows и .NET.

Видеозапись семинара: https://vimeo.com/53323987.

0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
682
On SlideShare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
4
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

Введение в разработку многопоточных приложений

  1. 1. Введение в разработку многопоточных приложений Дмитрий Костиков Ведущий разработчик C# 31 октября 2012 года
  2. 2. 2/62
  3. 3. Поморгаем int main() { while(true) { EnableLedOne(); DisableLedOne(); } } 3/62
  4. 4. Поморгаем int Delay(int ms) { for(int i = 0; i < ms * k ; i++){} } int main() { while(true) { EnableLedOne(); Delay(300); DisableLedOne(); Delay(900); } } 4/62
  5. 5. Одна лампочка 300 900 300 900 300 t(мс) 5/62
  6. 6. 6/62
  7. 7. Две лампочки 7/62 300 900 300 900 300 t(мс) 250 750 250 250750 750
  8. 8. Две лампочки 300 900 300 900 300 t(мс) 250 750 250 250750 750 50 мс 8/62
  9. 9. Очередь заданий Task1, через 50мс Task2, через 100мс Task1, сейчас Task2, через 50мс Task2, сейчас t(мс) 0 10050 9/62
  10. 10. Поморгаем с планировщиком void EnableLedOneTask() { EnableLedOne(); RegisterDelayedTask(DisableLedOneTask, 300); } void DisableLedOneTask() { DisableLedOne(); RegisterDelayedTask(EnableLedOneTask, 900); } int main() { RegisterDelayedTask(EnableLedOneTask, 0); RegisterDelayedTask(EnableLedTwoTask, 0); while(true) { Delay(50); ExecuteTaskByTime(); } } 10/62
  11. 11. Sleep void LedOne () { while(true) { EnableLedOne(); Sleep(300); DisableLedOne(); Sleep(600); } } 11/62
  12. 12. Sleep void LedOneThread() { B202 while(true) B203 { B204 EnableLedOne(); B205 Sleep(300); B207 DisableLedOne(); B208 Sleep(600); B209 } } 12/62
  13. 13. Sleep void LedOneThread() { int i = GetOnTime(); int j = GetOffTime(); while(true) { EnableLedOne(); Sleep(i); DisableLedOne(); Sleep(j); } } 13/62
  14. 14. Стек и регистры j Регистры Стек i 14/62
  15. 15. Стек и регистры Стек h j 15/62 Регистры g
  16. 16. Стеки и регистры Регистры i j Стек потока 1 i 16/62
  17. 17. Стеки и регистры Регистры Стек потока 2 g hj Стек потока 1 i g 17/62
  18. 18. Поток  Физически – процедура потока, служебные данные и стек  Логически – виртуальный процессор 18/62
  19. 19. Типы многопоточности Sleep() Sleep() Поток 1 Поток 2 t t 19/62
  20. 20. Типы многопоточности Кванты Sleep() Поток 2 t t Квант кончился Квант кончился 20/62 Поток 1
  21. 21. Создание потоков  .NET: class Thread  WinAPI: CreateThread/TerminateThread static void Main(string[] args) { Thread thread = new Thread(DoA); thread.Start(); } static void DoA() { … } 21/62
  22. 22. Пул потоков  WinAPI: QueueUserWorkItem  .NET: class ThreadPool static void Main(string[] args) { ThreadPool.QueueUserWorkItem(DoA); } static void DoA(object state) { } 22/62
  23. 23. 23/62
  24. 24. 24/62
  25. 25. GUI void OnClick() { ThreadPool.QueueUserWorkItem(DoA); } void DoA(object state) { resultTextBox.Text = ComputeSmth(); } 25/62
  26. 26. GUI Очередь сообщений Поток окна Перерисуй окно Кликнули мышкой 26/62
  27. 27. GUI Очередь сообщений Поток окна Перерисуй окно Кликнули мышкой Обработай окончание вычисления 27/62
  28. 28. GUI static void DoA(object state) { _result = ComputeSmth(); resultTextBox.BeginInvoke(SetResul t); } static void SetResult (object state) { resultTextBox.Text = _result; } 28/62
  29. 29. 29/62
  30. 30. Проблемы многопоточности  Проблемы корректности  Проблемы живучести 30/62
  31. 31. Состояние гонки _i++; MOV EAX , [_ i ] INC EAX MOV [ _i ] , EAX 31/62
  32. 32. «Одновременное» выполнение Поток 1 MOV EAX , [ i ] INC EAX MOV [ i ] , EAX Поток 2 MOV EAX , [ i ] INC EAX MOV [ i ] , EAX t 32/62
  33. 33. Многопроцессорная машина Процессор 1 Процессор 2 Память Кэш 1 Кэш 2 i = 2i = 2 i = 3i = 3 33/62 i = 2 i = 2i = 3 i = 3
  34. 34. Многопроцессорная машина Процессор 1 Процессор 2 Память Кэш 1 Кэш 2 i = 2i = 2 i = 2 i = 2i = 3 i = 3 i = 3i = 3 Когерентность кэша 34/62
  35. 35. Последовательное выполнение Поток 1 Enter() MOV EAX , [ i ] INC EAX MOV [ i ] , EAX Exit() Поток 2 Enter() INC EAX MOV EAX , [ i ] MOV [ i ] , EAX t 35/62
  36. 36. Критический регион … EnterCriticalRegion() _i++; LeaveCriticalRegion() … 36/62
  37. 37. Критический регион int taken = 0; void EnterCriticalRegion() { while(taken != 0) {} taken = 1; } void LeaveCriticalRegion() { taken = 0; } 37/62
  38. 38. 38/62
  39. 39. Строгое чередование const int N = 2; int turn = 0; void EnterCriticalRegion (int i) { while (turn != i ) {} } void LeaveCriticalRegion (int i) { turn = (i + 1) % N; } 39/62
  40. 40. Алгоритм Петерсона (1981) bool flags[2]; int turn = 0; void EnterCriticalRegion (int i) { flags [ i ] = true ; turn = 1 - i ; while ( flags[1 - i] && turn != i ) {} } void LeaveCriticalRegion (int i) { flags [i] = false; } 40/62
  41. 41. CAS long CompareExchange(long *Destination, long Exchange, long Comparand); int taken = 0; void EnterCriticalRegion() { while( CompareExchange((&taken ,1 , 0)) {} } void LeaveCriticalRegion() { taken = 0; } 41/62
  42. 42. Interlocked Interlocked.Increment(ref _i); 42/62
  43. 43. Mutex static Mutex _mutex = new Mutex(); static void DoA(object state) { _mutex.WaitOne(); _i++; _mutex.ReleaseMutex(); } 43/62
  44. 44. Monitor void EnterCriticalRegion() { for(int i = 0; i < 1000 && ! CompareExchange((&taken ,1 , 0)) ; i++) {} if(taken == 0) { _mutex.WaitOne(); } } 44/62
  45. 45. Monitor private static object _lockObject; static void DoA(object state) { lock(_lockObject) { ThreadUnsafeOperation(); } } WinAPI: EnterCriticalSection/LeaveCriticalSection 45/62
  46. 46. Неблокирующая синхронизация 46/62
  47. 47. Фокус flags[i] = true; turn = 1 - i; while (flags[1 - i] && turn != i) { } 47/62
  48. 48. Фокус flags[i] = true; turn = 1 - i; int tmp1 = flags[1 - i] int tmp2 = turn != I … 48/62
  49. 49. Фокус flags[i] = true; int tmp1 = flags[1 - i] … 49/62
  50. 50. Фокус 50/62 CPU1 flags[0] = true; int tmp1 = flags[1] … CPU2 flags[1] = true; int tmp1 = flags[0] …
  51. 51. Фокус CPU1 flags[0] = true; int tmp1 = flags[1] … CPU2 flags[1] = true; int tmp1 = flags[0] … 51/62
  52. 52. Многопроцессорная машина Процессор 1 Процессор 2 Память Кэш 1 Кэш 2 flags[0] = true flags[1] = true flags[0]=0 flags[1]=0 flags[0]=0 flags[1]=0 52/62
  53. 53. Фокус int tmp1 = flags[1 - i] flags[i] = true; … 53/62
  54. 54. Memory barrier flags[i] = true; Thread.MemoryBarrier(); int tmp1 = flags[1 - i] … 54/62
  55. 55. Lock flags[i] = true; turn = 1 - i; lock (_object) { while (flags[1 - i] && turn != i) { } } 55/62
  56. 56. 56/62
  57. 57. Deadlock public static void Transfer(BankAccount a, BankAccount b, decimal amount) { lock (a) { if (a.m_balance < amount) throw new Exception("Insufficient funds."); lock (b) { a.m_balance -= amount; b.m_balance += amount; } } } 57/62
  58. 58. Deadlock Поток 1 Поток 2 Lock(счет1) Lock(счет2) Lock(счет2) Lock(счет1) t 58/62
  59. 59. Банковский алгоритм public static void Transfer(BankAccount a, BankAccount b, decimal amount) { LockManager.LockAccounts(a, b) if (a.m_balance < amount) throw new Exception("Insufficient funds."); a.m_balance -= amount; b.m_balance += amount; } 59/62
  60. 60. Сортировка Поток 1 Поток 2 Lock(счет1) Lock(счет2) Lock(счет1) Lock(счет2) t 60/62
  61. 61. Что почитать  Concurrent Programming on Windows (Joe Duffy)  http://www.albahari.com/threading/  CLR via C# (Jeffrey Richter)  http://habrahabr.ru/users/rumatavz  Memory Barriers: a Hardware View for Software Hackers 61/62
  62. 62. Спасибо! Вопросы? Дмитрий Костиков dkostikov@custis.ru 62/62

×