2010-01-22 8 views
6

Не могли бы вы описать два метода синхронизации многопоточного доступа на запись на члене класса?Интервью Вопрос о .NET Threading

Пожалуйста, может кто-нибудь помочь мне, что это значит, и что это правильный ответ.

ответ

13

При изменении данных на C# что-то, что выглядит как одна операция, может быть скомпилировано в несколько инструкций. Возьмем следующий класс:

public class Number { 
    private int a = 0; 
    public void Add(int b) { 
     a += b; 
    } 
} 

Когда вы строите его, вы получите следующий IL код:

IL_0000: nop 
IL_0001: ldarg.0 
IL_0002: dup 
// Pushes the value of the private variable 'a' onto the stack 
IL_0003: ldfld  int32 Simple.Number::a 
// Pushes the value of the argument 'b' onto the stack 
IL_0008: ldarg.1 
// Adds the top two values of the stack together 
IL_0009: add 
// Sets 'a' to the value on top of the stack 
IL_000a: stfld  int32 Simple.Number::a 
IL_000f: ret 

Теперь у вас есть Number объект и два потока вызовите метод Add так:

number.Add(2); // Thread 1 
number.Add(3); // Thread 2 

Если вы хотите, чтобы результат был равен 5 (0 + 2 + 3), возникает проблема. Вы не знаете, когда эти потоки будут выполнять свои инструкции. Оба потока могут выполнить IL_0003 (толкание ноль в стек), прежде чем либо выполняет IL_000a (на самом деле изменения переменной-члена), и вы получите это:

a = 0 + 2; // Thread 1 
a = 0 + 3; // Thread 2 

Последняя нить, чтобы закончить «победы» и в конце процесса , a составляет 2 или 3 вместо 5.

Таким образом, вы должны убедиться, что один комплект инструкций заканчивается перед другим набором.Чтобы сделать это, вы можете:

1) Блокировка доступа к члену класса, пока она писалась, используя один из многих .NET synchronization primitives (как lock, Mutex, ReaderWriterLockSlim и т.д.), так что только один поток может работать на нем вовремя.

2) Переместите операции записи в очередь и обработайте эту очередь одним потоком. Как указывает Торарин, вам все равно придется синхронизировать доступ к очереди , если он не является потокобезопасным, но он стоит для сложных операций записи.

Существуют и другие методы. Некоторые (например, Interlocked) ограничены конкретными типами данных, и есть еще больше (например, те, которые обсуждаются в Non-blocking synchronization и Part 4 of Joseph Albahari's Threading in C#), хотя они сложнее: используйте их с осторожностью.

+0

+1 для ReadWriterLockSlim. Тем не менее, я думаю, что OP должен еще больше ознакомиться с многопоточным программированием, прежде чем полностью понять эту концепцию. Что касается вашего второго момента: вам нужно будет синхронизировать очередь, просто усложняя вашу проблему. – Thorarin

+0

Достаточно, и ваш ответ дает гораздо более глубокое представление об этих концепциях (хотя я пытаюсь использовать другой подход в обновлении). О очереди, правда, тоже нужно синхронизировать, хотя бывают случаи, когда такой подход упрощает вещи, а не усложняет их! –

12

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

Я предполагаю, что они подразумевают использование lock-statement (или SyncLock в VB.NET) против использования Monitor.

Возможно, вы найдете read this page для примеров и понимания концепции. Однако, если у вас нет опыта разработки многопоточных приложений, это, скорее всего, станет очевидным, если ваш новый работодатель проведет вас на тест. Это довольно сложный вопрос с множеством возможных ошибок, таких как deadlock.

Существует достойный MSDN page on the subject.

Могут быть другие варианты, в зависимости от типа переменной-члена и способа их изменения. Например, приращение целого числа можно выполнить с помощью метода Interlocked .Increment.

Как упражнение и демонстрация проблемы, попробуйте написать приложение, которое запускает 5 одновременных потоков, увеличивая общий счетчик миллион раз на поток. Предполагаемый конечный результат счетчика составит 5 миллионов, но это (возможно) не то, что вы в итоге получите :)

Редактировать: сделана быстрая реализация сама (download). Образец выходного сигнала:

Unsynchronized counter demo: 
expected counter = 5000000 
actual counter = 4901600 
Time taken (ms) = 67 

Synchronized counter demo: 
expected counter = 5000000 
actual counter = 5000000 
Time taken (ms) = 287 
+0

Спасибо за ответ. Я хотел бы узнать больше о многопоточности в .NET (C#). Infact Мне нужно знать больше о C# вообще. Не могли бы вы предложить мне одну книгу, которая может превратить обычного разработчика в разработчика следующего поколения. – Supremestar

+0

Никаких конкретных названий не приходит в голову, но я не очень много использую книги. На самом деле это довольно удивительно, что я предварительно заказал C# по глубине, второе издание. Хорошие вещи, но есть, вероятно, книги, более подходящие для ваших нужд. – Thorarin

+2

«C# in Depth» - потрясающая книга. –

0

Существует несколько способов, некоторые из которых упомянуты ранее.

  1. ReaderWriterLockSlim - мой предпочтительный метод. Это дает вам тип блокировки базы данных и позволяет обновлять (хотя синтаксис для этого неверен в MSDN в последний раз, когда я смотрел и очень неочевиден)
  2. Заблокированные операторы. Вы обрабатываете чтение как запись и просто препятствуете доступу к переменной
  3. Операции с блокировкой. Это выполняет операции над типом значения на атомном шаге. Это может быть использовано для блокировки многопоточности (на самом деле не рекомендовал бы это)
  4. мьютексов и семафоров (не использовали их)
  5. заявления Monitor (это, по существу, как работает ключевое слово замок)

Хотя я не хочу отрицать другие ответы, я не буду доверять никому, что не использует один из этих методов. Приносим извинения, если я забуду.