locked
lock внутри async метода RRS feed

  • Вопрос

  • Я не особо силен в многопоточном программировании. Поэтому вопрос: можно ли использовать lock внутри async метода. Вот так:
    private Dictionary<string, ClientController> clients = new Dictionary<string, ClientController>();
            private object locker = new object();
    
    public async void Add(ClientController client)
            {
                await new Task(() =>
                {
                    lock (locker)
                    {
                        clients.Add(client.Id, client);
                    }
                });
            }
    
            public async Task<int> Count()
            {
                return await new Task<int>(() =>
                {
                    lock (locker)
                    {
                        return clients.Count;
                    }
                });
            }
    если нельзя, то как правильно сделать асинхронную коллекцию, с потокобезопасным изменением?

    24 сентября 2014 г. 13:53

Ответы

  • lock — это просто обёртка над Monitor.Enter и Monitor.Exit, так что использовать lock внутри async метода можно. Однако есть несколько вопросов касательно Вашего кода:
    1. Почему
      public async void Add(...)
      а не
      public async Task Add(...)
      Как вызвавший метод сможет узнать о завершении работы, если Вы не возвращаете никакого объекта?
    2. await new Task(...)
      Задачу Вы создали, но не запустили, так что ждать её можно вечно.
    3. Зачем вообще использовать асинхронные методы, если всё, что Вы делаете, это создание и ожидание одной задачи?
      public Task Add(ClientController client) {
          return Task.Factory.StartNew(() => {
              lock(locker) {
                  clients.Add(client.Id,client);
              }
          });
      }
      
      public Task<int> Count() {
          return Task.Factory.StartNew(() => {
              lock(locker) {
                  return clients.Count;
              }
          });
      }
    4. А есть ли вообще смысл использовать здесь асинхроннось? Расходы на создание задачи вполне могут превышать расходы на добавление элемента в коллекцию.

    24 сентября 2014 г. 16:38

Все ответы

  • Я не особо силен в C#, но если я правильно понял работу lock, то да можно и в принципе нужно.

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

    Ну пусть гуру C# еще подтвердят.


    VB.Net - WPF, WinRT, WP

    24 сентября 2014 г. 14:22
    Отвечающий
  • lock — это просто обёртка над Monitor.Enter и Monitor.Exit, так что использовать lock внутри async метода можно. Однако есть несколько вопросов касательно Вашего кода:
    1. Почему
      public async void Add(...)
      а не
      public async Task Add(...)
      Как вызвавший метод сможет узнать о завершении работы, если Вы не возвращаете никакого объекта?
    2. await new Task(...)
      Задачу Вы создали, но не запустили, так что ждать её можно вечно.
    3. Зачем вообще использовать асинхронные методы, если всё, что Вы делаете, это создание и ожидание одной задачи?
      public Task Add(ClientController client) {
          return Task.Factory.StartNew(() => {
              lock(locker) {
                  clients.Add(client.Id,client);
              }
          });
      }
      
      public Task<int> Count() {
          return Task.Factory.StartNew(() => {
              lock(locker) {
                  return clients.Count;
              }
          });
      }
    4. А есть ли вообще смысл использовать здесь асинхроннось? Расходы на создание задачи вполне могут превышать расходы на добавление элемента в коллекцию.

    24 сентября 2014 г. 16:38
  • 1. да, вы правы, спасибо за поправку

    3. в классе есть еще и другие методы, я показал только эти 2 чтобы не нагромождать вопрос, а еще есть удаление, проверка на существование, и возврат массива элементов, Select и Where как в linq

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

    25 сентября 2014 г. 4:47
  • Использовать асинхроннось в данном случае лишено всякого смысла. Использовать возможности языка/платформы лишь только потому, что они есть – плохая практика.

    Сделаем содержимое сообщества лучше, вместе!

    25 сентября 2014 г. 5:46
    Модератор
  • private Dictionary<string, ClientController> clients = new Dictionary<string, ClientController>();

    private IDicitionary<string, ClientController> clients = new ConcurrentDictionary<string, ClientController>();

    25 сентября 2014 г. 6:41
  • Вообще если нужно лочить в асинк методах, то можно юзать такое - Async Lock
    25 сентября 2014 г. 9:02