Обязательна ли блокировка при использовании задач? - PullRequest
0 голосов
/ 11 апреля 2019

В настоящее время я разрабатываю приложение в WinForm C # для отображения значений с устройства, но мне нужно найти правильный путь.

Я запускаю задачу, которая представляет собой бесконечный цикл в конструкторе Mainform.Эта задача читает данные с устройства и записывает их в общие переменные.

Mainform читает эти общие переменные для отображения данных.

Поскольку задача только запись, а Mainformтолько чтение общих переменных мне нужно использовать инструкцию блокировки общих переменных?Какой риск, если не использовать Lock?

Ниже приведен запуск задачи в конструкторе Mainform.

Спасибо!

Task.Run(() =>
{
    while (true)
    {
        try
        {
            ReadDeviceData();
        }
        catch (Exception e)
        {
            Dispatcher.CurrentDispatcher.Invoke(() => 
                MessageBox.Show(e.Message +"\n\n"+ e.StackTrace, "task exception\n"));
        }                        

        Thread.Sleep(200);
    }
});

Ответы [ 4 ]

0 голосов
/ 11 апреля 2019

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

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

0 голосов
/ 11 апреля 2019

Это зависит ReadDeviceData.

Чтение int, bool или object('s reference) безопасно в модели памяти C #.

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

0 голосов
/ 11 апреля 2019

Ключевое слово lock не блокирует переменные. Он определяет часть кода, которая может быть выполнена только в одном потоке за раз. Например, из этих двух кодовых блоков только один может выполняться в любой момент и только в одном потоке:

lock (lockObject)
{
    this.A = random.Next();
    this.B = random.Next();
    this.C = this.A + this.B;
}

lock (lockObject)
{
    Console.WriteLine("{0}+{1}={2}", this.A, this.B, this.C);
}

Таким образом, в любой данный момент только один поток читает или записывает переменные-члены A, B и C.

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

this.A = random.Next();
this.B = random.Next();
Console.WriteLine("{0}+{1}={2}", this.A, this.B, this.C);
this.C = this.A + this.B;

Как вы можете ясно видеть, эта программа плохо работает по математике.

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

var results = new SomeClass
{
    A = random.Next(),
    B = random.Next()
};
results.C = results.A + results.B;
this.LatestResults = results;

Теперь единственная общая переменная (поскольку каждый поток получает свои локальные переменные) - LatestResults. Который может быть обновлен в одной атомарной операции. В этом случае блокировка не требуется.

0 голосов
/ 11 апреля 2019

Если только один поток записывает в переменную, вам не нужно блокировать.Блокировка рекомендуется при одновременной попытке записи в одну переменную.

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/lock-statement

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...