C # Многопоточность конфликтов на общих переменных - PullRequest
0 голосов
/ 20 ноября 2018

Моя проблема заключается в следующем: у меня работает графический интерфейс в своем собственном потоке 1, устанавливающий некоторые параметры конфигурации.

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

Конфигурация содержит переменные, такие как bool и Словари.

Какой безопасный способ решения этой проблемы?

Пока мои мысли: класс, содержащий текущую конфигурацию в некоторой статической переменной Instance:

public class Config
{
   static Config Instance;
   int a;
   Dictionary<int, int> b;
   ...
   public void update(Config newConfig) {}
}

, поэтому все классы в потоке 2 всегда могут получить доступ к новейшим настройкам.

Функция обновления вызывается, когда я нажимаю что-то вроде кнопки «apply» в GUI, чтобы применить новейшие настройки.

Но как насчет безопасности потока?Как установить переменные только тогда, когда thread2 не читает, и заставить поток 2 ждать, когда переменные установлены?У меня нет опыта работы с многопоточностью в c #, и я знаю, что, вероятно, придется заблокировать переменные, когда они используются каким-то потоком.или есть какой-то другой умный подход, как это реализовать?

Ответы [ 2 ]

0 голосов
/ 21 ноября 2018

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

Один из шагов - заставить другие ваши потоки создавать новые экземпляры Config вместо того, чтобы все они делились.Что ваш GUI (форма?) Может получить совершенно новый экземпляр Config.

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

public Config MyConfigInstance { get; set; }

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

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

Это может не быть проблемой в вашем приложении.Может быть, вы точно знаете, какие потоки имеют доступ к экземплярам каких классов.Но если что-то становится более сложным, опять же, мы бы предпочли избежать проблемы, чем пытаться отслеживать ее.

С этой целью, если несколько потоков будут совместно использовать экземпляры класса, часто предпочтительнее сделатькласс неизменный.Это примерно так:

public class MyImmutableClass
{
    public MyImmutableClass(string stringValue, int intValue)
    {
        StringValue = stringValue;
        IntValue = intValue;
    }

    public string StringValue { get; }
    public int IntValue { get; }
}

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

Еще одно преимущество неизменного класса заключается в том, что если значения задаются только в конструкторе, вы можете проверить их.Как и в приведенном выше примере, вы можете выдать исключение, если stringValue равно нулю.Таким образом, вы всегда знаете, что класс находится в допустимом состоянии.Невозможно создать экземпляр, который находится в недопустимом состоянии.

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

0 голосов
/ 20 ноября 2018

Вы ищете шаблон проектирования синглтона:

public sealed class Config
{
    private static Config instance = null;
    private static readonly object padlock = new object();

    #region ConfigProperties
    //put public properties here to represent config values
    public int a { get; set; }
    public Dictionary<int, int> b { get; set; }
    #endregion ConfigProperties

    Config()
    {
        ReadConfig();
    }

    public static Config Instance
    {
        get
        {
            lock (padlock)
            {
                if (instance == null)
                {
                    instance = new Config();
                }
                return instance;
            }
        }
    }
    public void ReadConfig()
    {
        //read from your config file and set properties
    }
    public void SaveConfig()
    {
        //write properties back into your config file
    }
}

Этот класс является поточно-ориентированным, что означает, что вы можете использовать Config.Instance., а затем любые ваши свойства конфигурации, и они будут безопасно доступны безвызывая проблемы между потоками.

Более элегантные решения и более подробные объяснения здесь .

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