Dispatcher.Invoke的竞争条件

时间:2013-05-01 13:29:06

标签: c# wpf multithreading

我有一个WritableBitmap,我想得到它的尺寸。因为该对象由另一个线程拥有,所以我们必须通过Dispatcher。我试过这个:

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修改了Tuple.Create的返回值,它永远不会为空。

我在这里缺少什么?导致这种竞争状况的原因,我该怎么办呢?

2 个答案:

答案 0 :(得分:0)

修改

抱歉给出了错误的答案。

似乎你想从另一个线程获取存在于gui线程中的WriteableBitmap的依赖属性。

你可以试试这个:

    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();

    }

我刚试过,效果很好。

答案 1 :(得分: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,图书[12]以及试图重现此问题的小玩具程序都表明情况并非如此; Invoke应该在调用线程上同步。

我无法提出真正的解释;如果有人有,我全都耳朵:)。

编辑用更连贯的内容取代原来的,有点散漫的答案。