2013-05-01 5 views
2

У меня есть WritableBitmap, и я хочу получить его размеры. Поскольку объект принадлежит другому потоку, мы должны пройти Диспетчер. Я попытался это:Состояние гонки с Dispatcher.Invoke

int targetPixelWidth = 0; 
int targetPixelHeight = 0; 

writeableBitmap.Dispatcher.Invoke(new Action(() => 
{ 
    targetPixelWidth = writeableBitmap.PixelWidth; 
    targetPixelHeight = writeableBitmap.PixelHeight; 
})); 

// Do something with targetPixelWidth and targetPixelHeight 

Однако, иногда это не удается: значения часто остается 0, несмотря на то, что реальные значения различны.

Думая, что это может быть проблемой нарезание резьбы, я изменил код следующим образом:

var bitmapInfo = (Tuple<int, int>)writeableBitmap.Dispatcher.Invoke(new Func<Tuple<int, int>>(
    () => Tuple.Create(writeableBitmap.PixelWidth, writeableBitmap.PixelHeight) 
)); 

Debug.Assert(bitmapInfo != null, "Obviously, this should pass."); 

targetPixelWidth = bitmapInfo.Item1; 
targetPixelHeight = bitmapInfo.Item2; 

// Do something with targetPixelWidth and targetPixelHeight 

Но сейчас, bitmapInfo иногда нуль. Что странно, потому что (согласно документации) Invoke должен возвращать только null, если у делегата нет возвращаемого значения, что очевидно в этом случае. У меня даже Debug.Assert ed возвращаемое значение Tuple.Create, и оно никогда не было нулевым.

Что мне здесь не хватает? Что вызывает это состояние гонки, и что я могу с этим поделать?

+0

try() => вернуть новый Tuple.Create (writeableBitmap.PixelWidth, writeableBitmap.PixelHeight); – makc

+0

Я сделал; к сожалению, не изменилось. – jqno

+0

Вы пытались помещать Debug.Assert или регистрироваться внутри выражения lambda? – makc

ответ

0

Это работает, хотя я не знаю, почему:

ManualResetEventSlim mre = new ManualResetEventSlim(false); 

int targetPixelWidth = 0; 
int targetPixelHeight = 0; 

writeableBitmap.Dispatcher.Invoke(new Action(() => 
{ 
    try { 
     targetPixelWidth = writeableBitmap.PixelWidth; 
     targetPixelHeight = writeableBitmap.PixelHeight; 
    } 
    finally { 
     mre.Set(); 
    } 
})); 

mre.Wait(); 
// Do something with targetPixelWidth and targetPixelHeight 

Кто-то (кто отправил ответ на этот вопрос, но потом удалил его), предположил, что Invoke является синхронным на GUI потоке, а не на поток, который вызывает Invoke. Если это правда, это объясняет, почему это работает. Однако the documentation, книги [1, 2] и маленькие игрушки, пытающиеся воспроизвести эту проблему, все предполагают, что это не так; Invoke должен быть синхронным на вызывающем потоке.

Я не смог придумать настоящего объяснения; если у кого-то есть, я все уши :).

EDIT Заменен мой оригинальный, несколько бессвязный ответ с чем-то более последовательным.

+0

В документации указано, что 'Invoke - это синхронная операция; поэтому управление не будет возвращаться к вызывающему объекту до тех пор, пока не будет возвращено обратное обращение », а возвращаемое значение - это« возвращаемое значение из вызываемого делегата или null, если у делегата нет никакого возвращаемого значения ». можете процитировать документацию, в которой говорится, что его синхронизация только по потоку пользовательского интерфейса, спасибо – makc

+0

«Выполняет указанный делегат с указанными аргументами ** синхронно в потоке, который диспетчер связан с **» (выделение мое). Кажется, что это противоречит цитируемой вами документации. Тем не менее, реальность также, похоже, конфликтует с ней ... :( – jqno

+0

Думаю, мне придется проверить это, чтобы поверить, она решает вашу проблему так: +1 – makc

0

EDIT

жаль дали неправильный ответ.

Кажется, что вы хотите получить свойство зависимостей WriteableBitmap, которое живет в потоке gui из другого потока.

вы можете попробовать это:

private void Window_Loaded(object sender, RoutedEventArgs e) 
    { 
     var writeableBitmap = new WriteableBitmap(100, 100, 300, 300, PixelFormats.Bgra32, null); 
     _image.Source = writeableBitmap; 

     new Thread(() => 
      { 
       Thread.Sleep(1000); 

       var pixelHeigth = (Int32)writeableBitmap.Dispatcher.Invoke(
                DispatcherPriority.Background, 
                (DispatcherOperationCallback)(arg => ((WriteableBitmap)arg).PixelHeight), writeableBitmap); 
       Debug.Print("PixelHeight:" + pixelHeigth); 

      }).Start(); 

    } 

Я просто попытался, он хорошо работает.

+0

Как получить доступ к базовому свойству зависимостей, отличному от простого вызова «нормального» доступа в этом случае? – jqno

+0

@jqno извините за неправильный ответ, я только что отредактировал ответ –

+0

Я не уверен, был ли это неправильный ответ, я просто не понял его :). Завтра я попробую, чтобы посмотреть, работает ли это. (По дороге домой.) Спасибо! – jqno

Смежные вопросы