Форум программистов, компьютерный форум, киберфорум
Наши страницы
C# .NET
Войти
Регистрация
Восстановить пароль
 
Casper-SC
Эксперт .NET
3620 / 1829 / 355
Регистрация: 27.03.2010
Сообщений: 5,141
Записей в блоге: 1
1

Заменить volatile на Thread.MemoryBarrier. Код приведён. Как оптимизировать обращения для чтения к volatile полю класса?

13.08.2018, 05:52. Просмотров 282. Ответов 17
Метки нет (Все метки)

Не совсем понятна мне пока что работа Thread.MemoryBarrier. Знаю, что можно оптимизировать обращения к полю _cancellation, смотрел выступление на DotNext человека, который приводил примеры, но так как я с его объяснений толком не понял, как это внутри работает, то боюсь накосячить. Поэтому у меня вопрос, как во-первых этот код изменить, чтобы обращения на чтение _cancellation не приводили к ошибкам, так как методы работают в разных потоках, а конкретно CancelWorkAsync вызывается из UI потока, DoWorkAsync из одного из потоков пула потоков и innerWorker.ProgressChanged генерируется тоже из одного из потоков пула потоков.

InnerWorkerProgressChanged вызывается часто, что по идее должно немного сказываться на производительности, так как мы постоянно читаем из ОЗУ это поле, игнорируя кэш процессора (я это так понимаю).

В идеале, я бы не против услышать хорошее объяснение, как это работает. Если сам нагуглю хорошее объяснение или где-то в книге вычитаю, то выложу сюда.

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
    public class SomeWorker
    {
        private volatile CancellationTokenSourceWrapper _cancellation;
 
        public async Task DoWorkAsync(int id)
        {
            if (_cancellation != null)
            {
                throw new InvalidOperationException("DoWork already started");
            }
 
            _cancellation = new CancellationTokenSourceWrapper();
 
            //IInnerWorker innerWorker = null;
            try
            {
                //innerWorker = factory.CreateWorker(/* параметры */);
                //innerWorker.ProgressChanged += InnerWorkerProgressChanged;
                //await innerWorker.DoWorkAsync(id, _cancellation.Token);
            }
            finally
            {
                //if (innerWorker != null)
                //    innerWorker.ProgressChanged -= InnerWorkerProgressChanged;
 
                _cancellation.Release();
                _cancellation.Dispose();
                _cancellation = null;
            }
        }
 
        private void InnerWorkerProgressChanged(int progress)
        {
            var cancellation = _cancellation;
            bool stopUpdates = cancellation != null && cancellation.StopUpdatesRequested;
 
            if (!stopUpdates)
            {
                // Что-то обновляем.
            }
        }
 
        public async Task<bool> CancelWorkAsync()
        {
            var cancellation = _cancellation;
            if (cancellation != null)
            {
                cancellation.Cancel(true);
                await cancellation.WaitForCancellation();
                return true;
            }
            else
            {
                return false;
            }
        }
    }
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
    public class CancellationTokenSourceWrapper : IDisposable
    {
        private readonly CancellationTokenSource _cancellationTokenSource;
        private readonly TaskCompletionSource<bool> _taskCompletionSource;
 
        public CancellationTokenSourceWrapper()
        {
            _cancellationTokenSource = new CancellationTokenSource();
            _taskCompletionSource = new TaskCompletionSource<bool>();
        }
 
        public CancellationToken Token
        {
            get { return _cancellationTokenSource.Token; }
        }
 
        public bool StopUpdatesRequested { get; private set; }
 
        public void Cancel(bool stopUpdates)
        {
            StopUpdatesRequested = stopUpdates;
            _cancellationTokenSource.Cancel();
        }
 
        public async Task WaitForCancellation()
        {
            await _taskCompletionSource.Task;
        }
 
        public void Release()
        {
            _taskCompletionSource.SetResult(true);
        }
 
        public void Dispose()
        {
            _cancellationTokenSource.Dispose();
        }
    }
Добавлено через 9 минут
Вообще, если посмотреть внимательно, то InnerWorkerProgressChanged и так не сработает никогда и там такая проверка не нужна (там код более замороченный и этот метод ещё вызывается из других мест, возможно, при детальном анализе окажется, что и правда проверка не нужна, но на такой анализ пока нет времени и желания особого).

Но предположим, что она нужна. Тут главное суть вопроса.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
13.08.2018, 05:52
Ответы с готовыми решениями:

Использование Volatile
В общем, вопрос как его использовать. Есть такой тестовый код: using System;...

Не могу подключиться к dbf-файлу: Общий сбой Не удается открыть раздел реестра 'Temporary (volatile) Jet DSN for process 0xb9c Thread 0xcf4 DBC 0x1157
День добрый. У меня на машине установлен WinGate. Вот я и захотел сделать,...

General error Unable to open registry key 'Temporary (volatile) Jet DSN for process 0x388 Thread 0x980 DBC 0x1166bf9c Jet'.
подскажите как попбороть ошибку: ------------------------------------...

Ошибка при создании ADODB.Connection: : Unable to open registry key 'Temporary (volatile)
Иногда при создани ADO-шного коннекшена появляется ошибка : Unable to open...

И снова volatile. Глобальный массив, изменяемый в обработчике прерывания, должен быть volatile?
Всем привет. Имеется официальный код примера на чип-трансивер nrf24LE1 от...

17
maximka777
9 / 7 / 5
Регистрация: 13.08.2018
Сообщений: 35
13.08.2018, 13:51 2
Цитата Сообщение от Casper-SC Посмотреть сообщение
так как мы постоянно читаем из ОЗУ это поле, игнорируя кэш процессора
При чем тут вообще кэш и озу процессора. volatile гарантирует что один поток считает то, что другой туда запишет.
Цитата Сообщение от Casper-SC Посмотреть сообщение
Заменить volatile на Thread.MemoryBarrier
Зачем менять ? Быстрее код работать не станет. MemoryBarrier гарантирует порядок выполнения инструкций до/после него. Применять нужно там , где действительно нужно.
0
TopLayer
785 / 584 / 302
Регистрация: 23.10.2016
Сообщений: 1,391
Завершенные тесты: 7
13.08.2018, 15:12 3
Цитата Сообщение от Casper-SC Посмотреть сообщение
Не совсем понятна мне пока что работа Thread.MemoryBarrier
Он делает 3 вещи:
1) Запрещает компилятору и процессору переупорядочивать операции чтения/записи относительно этой инструкции.
2) Заставляет текущий процессор сообщить другим процессорам о всех сделанных изменениях (инвалидация строк кеша)
3) Заставляет текущий процессор обработать все сообщения других процессоров

Цитата Сообщение от Casper-SC Посмотреть сообщение
так как методы работают в разных потоках
Как обычно, если на этом форуме появляется пример кода с volatile, то он 90% содержит ошибку. Тут не исключение: один поток выполнит .Dispose() в DoWorkAsync, а второй отработает с отдиспоженным объектом в CancelWorkAsync. И только потом первый поток обнулит поле.

Цитата Сообщение от Casper-SC Посмотреть сообщение
InnerWorkerProgressChanged вызывается часто, что по идее должно немного сказываться на производительности, так как мы постоянно читаем из ОЗУ это поле, игнорируя кэш процессора (я это так понимаю).
Не уверен, что проброс чтения идёт прямо в ОЗУ, а не в кеш более высокого уровня. Я думаю обращение к volatile полю идёт на порядок быстрее, чем отрабатывает механизм вызова метода. Вот если бы в цикле была бы работа с волатильным полем, тогда, может быть, можно было бы что-то говорить о падении производительности. Следующий тест показывает, что обращение к volatile полю ничтожно сказывается на производительностью, если рядом происходит вызов метода:
Кликните здесь для просмотра всего текста
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
class Inc
{
    private int _value;
    public int Value => _value;
    
    [MethodImpl(MethodImplOptions.NoInlining)]
    public void Increment()
    {
        _value += 1;
    }
}
 
class VolatileInc
{
    private volatile int _value;
    public int Value => _value;
    
    [MethodImpl(MethodImplOptions.NoInlining)]
    public void Increment()
    {
        _value += 1;
    }
}
 
class Diagnostics : IDisposable
{
    private readonly Stopwatch _sw;
    
    public Diagnostics()
    {
        _sw = Stopwatch.StartNew();
    }
    
    public void Dispose()
    {
        _sw.Stop();
        Console.WriteLine(_sw.Elapsed);
    }
}
 
void Main()
{
    int tries = 2000*1000*1000;
    
    var inc = new Inc();
    var volaInc = new VolatileInc();
    
    using (new Diagnostics())
    {
        for (int i = 0; i < tries; i++)
        {
            inc.Increment();        
        }
    }
    Console.WriteLine(inc.Value);
    
    using (new Diagnostics())
    {
        for (int i = 0; i < tries; i++)
        {
            volaInc.Increment();
        }
    }
    Console.WriteLine(volaInc.Value);
}
1
maximka777
9 / 7 / 5
Регистрация: 13.08.2018
Сообщений: 35
13.08.2018, 17:15 4
Цитата Сообщение от TopLayer Посмотреть сообщение
Не уверен, что проброс чтения идёт прямо в ОЗУ
Он может идти хоть в ПЗУ, к железу volatile ни какого отношения не имеет. Главная черта volatile по стандарту в том, что компилятору запрещается (оптимизировать) кешировать это значение.
чтобы такой код
C#
1
while(volatile){}
не превратился после оптимизации в такой
C#
1
2
reg=volatile;
while(reg){}
А по сути обращение к полю volatile это обычные операции с памятью. И кстати эти операции даже не атомарные. Так что не удивляйтесь тому что если один поток запишет 123, а другой считает 321.))
0
TopLayer
785 / 584 / 302
Регистрация: 23.10.2016
Сообщений: 1,391
Завершенные тесты: 7
13.08.2018, 17:33 5
Цитата Сообщение от maximka777 Посмотреть сообщение
И кстати эти операции даже не атомарные.
Для всех типов, к которым применим модификатор volatile, гарантируется атомарность чтения/записи.
0
maximka777
9 / 7 / 5
Регистрация: 13.08.2018
Сообщений: 35
13.08.2018, 17:39 6
Цитата Сообщение от TopLayer Посмотреть сообщение
Для всех типов, к которым применим модификатор volatile, гарантируется атомарность чтения/записи.
Гарантируется на программном или железном уровне ?
У меня операция сложения с volatile в дисассэмблере выглядет как
Assembler
1
add  dword ptr ds:[1614420h],1122h
че то префикса атомарности (lock) я здесь не вижу.
0
TopLayer
785 / 584 / 302
Регистрация: 23.10.2016
Сообщений: 1,391
Завершенные тесты: 7
13.08.2018, 18:02 7
Цитата Сообщение от maximka777 Посмотреть сообщение
Гарантируется на программном или железном уровне ?
Не знаю. Главное, что в спецификации C# это есть.
Reads and writes of the following data types are atomic: bool, char, byte, sbyte, short, ushort, uint, int, float, and reference types. In addition, reads and writes of enum types with an underlying type in the previous list are also atomic.
Добавлено через 9 минут
Цитата Сообщение от maximka777 Посмотреть сообщение
У меня операция сложения с volatile в дисассэмблере выглядет как
Я говорил про чтение/запись. Сложения это не касается.
0
maximka777
9 / 7 / 5
Регистрация: 13.08.2018
Сообщений: 35
13.08.2018, 18:19 8
Цитата Сообщение от TopLayer Посмотреть сообщение
Сложения это не касается.
Это че тогда выходит... Читай /записывай значит по мануалу атомарно, а если складывать то аблом .
0
TopLayer
785 / 584 / 302
Регистрация: 23.10.2016
Сообщений: 1,391
Завершенные тесты: 7
13.08.2018, 18:23 9
Цитата Сообщение от maximka777 Посмотреть сообщение
а если складывать то аблом
Да. Атомарность для read-modfy-write не гарантируется - нужно самому обеспечивать. В случае со сложением можно вызвать Interlocked.Add.
0
nicolas2008
256 / 241 / 85
Регистрация: 30.04.2009
Сообщений: 713
Завершенные тесты: 1
13.08.2018, 18:36 10
maximka777, почитай что об этом всем пишут в своих книгах Рихтер или Альбахари. А лучше несколько раз.
Если коротко использование volatile в твоем коде неправильное.
volatile не заменяет lock. cancellation != null && cancellation.StopUpdatesRequested - это минимум две (а может и три) операции.
volatile применяется только к конкретному обьекту, оно не влияет на операции чтения/записи полей этого обьекта. Т.е. обращение к примеру к свойству StopUpdatesRequested не будет безопасным в этом плане.
1
Casper-SC
Эксперт .NET
3620 / 1829 / 355
Регистрация: 27.03.2010
Сообщений: 5,141
Записей в блоге: 1
13.08.2018, 19:48  [ТС] 11
Ключевое слово volatile. Судя по примеру кода не нужно синхронизировать присвоение и чтение из volatile поля. Я помню ещё где-то про это читал, зашёл сюда, прочёл, что оказывается не атомарно присвоение, не разобравшись насинхронизировал и запушил. Поторопился . Готовлю ещё один коммит.

Цитата Сообщение от TopLayer Посмотреть сообщение
Как обычно, если на этом форуме появляется пример кода с volatile, то он 90% содержит ошибку. Тут не исключение: один поток выполнит .Dispose() в DoWorkAsync, а второй отработает с отдиспоженным объектом в CancelWorkAsync. И только потом первый поток обнулит поле.
Поправил я это так. То что Cancel могут вызвать несколько раз и последующие разы игнорируются - это нормально, так как, такой вызов по логике не получится сделать, а если и получится, то это уже будет ошибка на уровень выше этих классов. Dispose тут тоже не нужен особо.

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
    public class CancellationTokenSourceWrapper
    {
        private readonly object _syncCancellation = new object();
        private readonly TaskCompletionSource<bool> _taskCompletionSource;
        private volatile CancellationTokenSource _cancellationTokenSource;
 
        public CancellationTokenSourceWrapper()
        {
            _cancellationTokenSource = new CancellationTokenSource();
            _taskCompletionSource = new TaskCompletionSource<bool>();
        }
 
        public CancellationToken Token
        {
            get
            {
                lock (_syncCancellation)
                {
                    return _cancellationTokenSource.Token;
                }
            }
        }
 
        public bool StopUpdatesRequested { get; private set; }
 
        public void Cancel(bool stopUpdates)
        {
            if (_cancellationTokenSource != null)
            {
                lock (_syncCancellation)
                {
                    if (_cancellationTokenSource != null)
                    {
                        StopUpdatesRequested = stopUpdates;
                        _cancellationTokenSource.Cancel();
                    }
                }
            }
        }
 
        public async Task WaitForCancellation()
        {
            await _taskCompletionSource.Task;
        }
 
        public void Release()
        {
            _taskCompletionSource.SetResult(true);
 
            lock (_syncCancellation)
            {
                _cancellationTokenSource.Dispose();
                _cancellationTokenSource = null;
            }
        }
    }
Добавлено через 2 минуты
В общем, замутило меня вот это видео, в котором я увидел, что у поля убрали ключевое слово volatile, и используют MemoryBarrier. Возможно, я крайне невнимательно слушал.

https://youtu.be/Ogb8PuoNL_c?t=22m41s
Код из видео (Github)

Добавлено через 2 минуты
Цитата Сообщение от maximka777 Посмотреть сообщение
Он может идти хоть в ПЗУ, к железу volatile ни какого отношения не имеет. Главная черта volatile по стандарту в том, что компилятору запрещается (оптимизировать) кешировать это значение.
Ну, а побочный эффект от этого как раз как-то влияет на то, как отработает железо. Вот причём тут ОЗУ (компьютера) и кэш процессора.

Добавлено через 5 минут
Цитата Сообщение от nicolas2008 Посмотреть сообщение
volatile не заменяет lock. cancellation != null && cancellation.StopUpdatesRequested - это минимум две (а может и три) операции.
volatile применяется только к конкретному обьекту, оно не влияет на операции чтения/записи полей этого обьекта. Т.е. обращение к примеру к свойству StopUpdatesRequested не будет безопасным в этом плане.
Я же там к локальной переменной обращаюсь, почему это "обращение к примеру к свойству StopUpdatesRequested не будет безопасным"? Разве?

Добавлено через 56 секунд
Цитата Сообщение от nicolas2008 Посмотреть сообщение
volatile не заменяет lock
Он и не нужен, так как присвоение у ссылочных типов - это атомарная операция.
0
nicolas2008
256 / 241 / 85
Регистрация: 30.04.2009
Сообщений: 713
Завершенные тесты: 1
13.08.2018, 20:08 12
Цитата Сообщение от Casper-SC Посмотреть сообщение
Я же там к локальной переменной обращаюсь, почему это "обращение к примеру к свойству StopUpdatesRequested не будет безопасным"? Разве?
Поле класса это не локальная переменная.
Цитата Сообщение от Casper-SC Посмотреть сообщение
Он и не нужен, так как присвоение у ссылочных типов - это атомарная операция.
cancellation != null && cancellation.StopUpdatesRequested это присвоение?
А там где у делается присвоение, оно делается с проверкой на null. Проверка + присвоение это уже тоже не атомарная операция.
1
Casper-SC
Эксперт .NET
3620 / 1829 / 355
Регистрация: 27.03.2010
Сообщений: 5,141
Записей в блоге: 1
13.08.2018, 20:17  [ТС] 13
Цитата Сообщение от nicolas2008 Посмотреть сообщение
Поле класса это не локальная переменная.
Я присвоил локальной переменной cancellation значение из volatile поля _cancellation, и получил гарантированно актуальное значение - либо ссылку на экземпляр класса, либо null, проверил локальную переменную на null и если не null, то получил значение через свойство (по факту метод get_StopUpdatesRequested())

C#
1
2
3
4
5
6
7
8
9
10
        private void InnerWorkerProgressChanged(int progress)
        {
            var cancellation = _cancellation;
            bool stopUpdates = cancellation != null && cancellation.StopUpdatesRequested;
 
            if (!stopUpdates)
            {
                // Что-то обновляем.
            }
        }
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
        public async Task<bool> CancelWorkAsync()
        {
            var cancellation = _cancellation;
            if (cancellation != null)
            {
                cancellation.Cancel(true);
                await cancellation.WaitForCancellation();
                return true;
            }
            else
            {
                return false;
            }
        }
0
nicolas2008
256 / 241 / 85
Регистрация: 30.04.2009
Сообщений: 713
Завершенные тесты: 1
13.08.2018, 20:36 14
Цитата Сообщение от Casper-SC Посмотреть сообщение
Я присвоил локальной переменной cancellation значение из volatile поля _cancellation, и получил гарантированно актуальное значение - либо ссылку на экземпляр класса, либо null, проверил локальную переменную на null и если не null, то получил значение через свойство (по факту метод get_StopUpdatesRequested())
Относительно локальной переменной cancelation согласен, не заметил.
Но суть от этого не меняется.
Во первых, StopUpdatesRequested не volative. Копирование значения в локальную переменную stopUpdates не гарантирует получения актуального значения.
Во вторых, пока вы возитесь с этой переменной, проверяя её на false, актуальное значение может поменяться. Критично ли для выполнения событыя InnerWorkerProgressChanged чтобы StopUpdatesRequested было false зависит от логики обработчика этого событыя.
0
TopLayer
785 / 584 / 302
Регистрация: 23.10.2016
Сообщений: 1,391
Завершенные тесты: 7
13.08.2018, 21:16 15
Цитата Сообщение от Casper-SC Посмотреть сообщение
В общем, замутило меня вот это видео, в котором я увидел, что у поля убрали ключевое слово volatile, и используют MemoryBarrier. Возможно, я крайне невнимательно слушал.
Не надо везде volatile заменять на MemoryBarier. Volatile намного быстрее, так как не будет вызовов методов. В видео другая ситуация: волатильность нужна только при создании синглентона, а с ключевым словом volatile, чтение всегда будет волатильно, что избыточно.

Лучше использовать инструкцию lock и не париться.

Вы кстати понимаете, что в метод DoWorkAsync могут одновременно два потока зайти и отработать, несмотря на проверку?

Еще, почему бы не использовать стандартный интерфейс IProgress<T>, для репорта прогресса.
Кликните здесь для просмотра всего текста
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class CancellableProgress<T> : Progress<T>
{
    private int _isReporting = 1;
    public bool IsReporting
    {
        get { return Interlocked.CompareExchange(ref _isReporting, 1, 1) == 1; }
        set { Interlocked.CompareExchange(ref _isReporting, value ? 1 : 0, value ? 0 : 1); }
    }
 
    protected override void OnReport(T value)
    {
        if (IsReporting)
        {
            base.OnReport(value);
        }
    }
}


Ещё бы точно понимать назначение SomeWorker. Может такое подойдёт:
Кликните здесь для просмотра всего текста
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
class SomeWorker
{
    private int _isRunning;
    private bool IsRunning
    {
        get { return Interlocked.CompareExchange(ref _isRunning, 1, 1) == 1; }
        set
        {
            bool wasRunning = Interlocked.CompareExchange(ref _isRunning, value ? 1 : 0, value ? 0 : 1) == 1;
            if (wasRunning && value)
            {
                throw new InvalidOperationException();
            }
        }
    }
    
    private volatile Task _runningTask = Task.CompletedTask;
    
    public async Task RunAsync(Task coldTask)
    {
        if (coldTask == null)
        {
            throw new ArgumentNullException(nameof(coldTask));
        }
        
        IsRunning = true;
        try
        {
            _runningTask = coldTask;
            coldTask.Start();
            await coldTask.ConfigureAwait(continueOnCapturedContext: false);
        }
        finally
        {
            IsRunning = false;
        }
    }
    
    public Task JoinAsync()
    {
        // avoid original task exceptions
        return _runningTask.ContinueWith(delegate { }, TaskContinuationOptions.ExecuteSynchronously);
    }
}
1
Casper-SC
Эксперт .NET
3620 / 1829 / 355
Регистрация: 27.03.2010
Сообщений: 5,141
Записей в блоге: 1
13.08.2018, 22:49  [ТС] 16
Цитата Сообщение от TopLayer Посмотреть сообщение
Вы кстати понимаете, что в метод DoWorkAsync могут одновременно два потока зайти и отработать, несмотря на проверку?
В теории да, по факту нет, ибо выше по стеку вызовов есть цикл, который берёт объекты по одному из ConcurrentDictionary<T>. Я эту проверку так на всякий случай сделал, мне даже в голову не приходило, что туда могут два потока зайти, так как этого не может быть в текущей реализации кода, который вызывает DoWorkAsync, но я понимаю, что это всё равно неправильно и нужно сделать всё по уму. Так что проверку сделаю нормальную.

Цитата Сообщение от TopLayer Посмотреть сообщение
Еще, почему бы не использовать стандартный интерфейс IProgress<T>, для репорта прогресса.
Он сначала и использовался, честно, не помню почему, но почему-то я его выпилил. По-моему потому что удобнее использовать интерфейс событием и несколько его реализаций. Я помню плевался сидел и-за этого IProgress<T>.

Добавлено через 11 минут
Цитата Сообщение от TopLayer Посмотреть сообщение
Ещё бы точно понимать назначение SomeWorker. Может такое подойдёт:
Это чуть позже посмотрю. Да SomeWorker это посредник, так скажем, который создаётся в виде свойства в одном классе, экземпляров которого создаётся много, каждый со своими данными, который... В общем, там заморочено немного.

Достаём из очереди someObject, много всего проделываем и вызываем DoWorkAsync. И вот в этом месте никак не могут зайти несколько потоков в DoWorkAsync.
C#
1
await someObject.SomeWorker.DoWorkAsync(/* параметры */);
0
Fleder
245 / 208 / 106
Регистрация: 09.12.2015
Сообщений: 631
14.08.2018, 08:43 17
Цитата Сообщение от Casper-SC Посмотреть сообщение
В общем, замутило меня вот это видео
Несколько смущает в нём один момент (на 11:12).
Мужчина рассказывает про "куки" для удобства работы с примитивами синхронизации.
В качестве таких обёрток предлагает использовать структуры, реализующие интерфейс IDisposable
и использовать их в "красивой" конструкции using.
Он подчёркивает важность выбора именно структур тем, что нет никаких накладных расходов на мемори трафик.
Но так ли это?
Ведь конструкция using разворачивается в код, который в блоке finally
вызовет метод Dispose() через интерфейсную ссылку.
А это значит, что такая структура будет упакована.
Дяденька об этом не знает?
Или это я туплю?
0
Cupko
455 / 444 / 125
Регистрация: 17.07.2012
Сообщений: 1,322
Записей в блоге: 1
Завершенные тесты: 2
14.08.2018, 10:47 18
Fleder, какой умный дяденька. Знает то, чего мы не знаем
Эрик Липерт утверждает, что при использовании using конструкции боксинга не будет.
1
14.08.2018, 10:47
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
14.08.2018, 10:47

Volatile
еще не понятен модификатор volatile/ не хотел открывать новую тему.

volatile
зачем нужно ключевое слово volatile?

volatile
Давайте внесем ясность и разберемся максимально подробно, что такое volatile. И...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
18
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2018, vBulletin Solutions, Inc.
Рейтинг@Mail.ru