Задача на многопоточность. Синхронизация читателей и писателей C#

Есть "сервер" в виде статического класса.
У него есть переменная count (тип int) и два метода, которые позволяют эту переменную читать и писать: GetCount() и AddToCount(int value). К серверу стучатся множество параллельных клиентов, которые в основном читают, но некоторые добавляют значение к count.

Нужно реализовать GetCount / AddToCount так, чтобы:

  • читатели могли читать параллельно, без выстраивания в очередь по локу;
  • писатели писали только последовательно и никогда одновременно;
  • пока писатели добавляют и пишут, читатели должны ждать окончания записи.

Подскажите как такое делать, или что посмотеть


Ответы (3 шт):

Автор решения: Qwertiy

Увеличение на 1:

Interlocked.Increment(ref count);

Увеличение на произвольное значение:

Interlocked.Add(ref count, difference);
→ Ссылка
Автор решения: aepot

Подскажите как такое делать, или что посмотеть

Смотрите код, это очень просто:

public static class MyServer
{
    private static int count;
    private static ReaderWriterLock locker = new ReaderWriterLock();

    public static void AddToCount(int value)
    {
        locker.AcquireWriterLock(Timeout.InfiniteTimeSpan);
        count += value;
        locker.ReleaseWriterLock();
    }

    public static int GetCount()
    {
        locker.AcquireReaderLock(Timeout.InfiniteTimeSpan);
        try
        {
            return count;
        }
        finally
        {
            locker.ReleaseReaderLock();
        }
    }
}

Сложно придумать пример, который бы наглядно показывал, что все работает, но я попробовал поиздеваться. Пусть четные потоки пишут, а нечетные читают.

static void Main(string[] args)
{
    Parallel.For(0, 50, i =>
    {
        if (i % 2 == 0) 
            MyServer.AddToCount(2);
        else
            Console.WriteLine(MyServer.GetCount());
    });
    Console.WriteLine(MyServer.GetCount());
    Console.WriteLine("Done.");
    Console.ReadKey();
}

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

2
8
12
10
4
6
20
22
24
26
28
30
32
34
38
40
36
14
12
44
16
46
18
48
30
50
Done.

Как бы я не пытался перенагрузить класс чтениями и записями из отдельных потоков, последнее число всегда правильно - данном случае 50.


Кстати, в случае с int проще пользоваться атомарными операциями типа Interlocked.Add.

→ Ссылка
Автор решения: DanBit
public static class MeServer
    {
        private static int count;
        private static ReaderWriterLockSlim locker = new ReaderWriterLockSlim();

        public static void AddToCount(int value)
        {
            locker.EnterWriteLock();
            count += value;
            locker.ExitWriteLock();
        }

        public static int GetCount()
        {
            locker.EnterReadLock();
            try
            {
                return count;
            }
            finally
            {
                locker.ExitReadLock();
            }
        }
    }
→ Ссылка