Подтвердить что ты не робот

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

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

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
4b9b3361

Ответ 1

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

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 генерирует полный забор;

Ответ 2

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

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

Я бы подумал превратить это в Operations с помощью объектов Task - это решит все это за один раз (например, верните задачу из вашего 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, но он должен работать с ошибками модульного синтаксиса с помощью неправильного написания/ввода текста. Я сделал