Безопасно ли делить локальную переменную между потоками (через закрытие обратного вызова)? - PullRequest
12 голосов
/ 02 сентября 2011

Я хочу сделать что-то вроде следующего - в основном я вызываю асинхронную операцию, которая вызовет обратный вызов в другом потоке, и я хочу дождаться, пока она завершит «inline». Меня беспокоит то, что изменения переменных, общих для потоков (bar & event), могут не синхронизироваться, например, из-за того, что они хранятся в регистрах. Если бы они были переменными-членами, я мог бы пометить их как volatile, но их нельзя использовать для локальных переменных, созданных в стеке. Я мог бы использовать переменные-члены, но я думаю, что его очиститель не загромождает мой класс, сохраняя все это локальным.

Bar bar = null;
ManualResetEvent event = new ManualResetEvent(false);

foo.AsyncOperation(new Action(()=>{    
    // This delegate will be called in another thread
    bar = ...
    event.Set();
}));

event.WaitOne(timeout);
// use bar

Ответы [ 2 ]

6 голосов
/ 02 сентября 2011

Да, это будет работать правильно.Прочитайте здесь

http://www.albahari.com/threading/part4.aspx

The following implicitly generate full fences: Setting and waiting on a signaling construct

и в сигнальных конструкциях включен ManualResetEvent.

Если вы хотитечтобы узнать, что такое full fence, на той же странице:

Полные заборы Самый простой вид барьера памяти - это полный барьер памяти (полный забор), который предотвращает любые виды перестановки команд или кэширования вокругэтот забор.Вызов Thread.MemoryBarrier создает полный забор;

4 голосов
/ 02 сентября 2011

Я думаю, что ваш код будет работать - закрытие переместится затем в кучу, даже если они были просто переменными стека (ManualReseetEvent наверняка не будет).

Но почему бы вам не поставить все послеevent.WaitOne () только внутри продолжения (блок был event.Set вызывается)?Я думаю, что это должен быть предпочтительный способ справиться с подобной ситуацией, и вы не столкнетесь с проблемами таким образом (вам вообще не нужен бар во внешнем блоке, и вы все равно можете использовать MRE для проверки).

Я бы подумал о том, чтобы превратить это в Операции с использованием объектов Задачи - это решило бы все это за один раз (например, вернуть Задачу из AsyncOperation).Затем вы можете дождаться результата задания и использовать возвращенный столбец ...

class Foo
{ 
 // ...
 private Task<Bar> AsyncOperation(Task<Bar> initializeBar)
 {
   return initializeBar
          .ContinueWith(
            bar => { 
                     /* do your work with bar and return some new or same bar */ 
                     return bar;
                   });
 }
}

и использовать его следующим образом:

var task = foo.AsyncOperation(Taks.Factory.StartNew(() => { /* create and return a bar */ }));
var theBar = task.Result; // <- this will wait for the task to finish
// use your bar

PS: закрытие в основном обернет ихпросто в объект класса;) PPS: мне трудно протестировать этот код без AsyncOperation, но он должен работать по модулю синтаксических ошибок при неправильной орфографии / наборе текста, которые я сделал

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