【问题标题】:WP8: Dispatcher.BeginInvoke doesn't execute an action on UI threadWP8:Dispatcher.BeginInvoke 不在 UI 线程上执行操作
【发布时间】:2014-03-31 18:49:47
【问题描述】:

我正在使用 MonoGame 将 Windows Phone 7 游戏移植到 Windows Phone 8。我的游戏使用 Texture2D.FromStream 从 Internet 加载图片,一旦它尚未在 MonoGame 中实现,我正在尝试建议的解决方法:在 UI 线程中加载带有 BitmapImage 的图片。

代码如下:

            Texture2D result = null;
            EventWaitHandle Wait = new AutoResetEvent(false);
            Deployment.Current.Dispatcher.BeginInvoke(() =>
            {
                BitmapImage image = new BitmapImage();
                image.SetSource(stream);
                WriteableBitmap writeImage = new WriteableBitmap(image);

                var index = 0;
                var pixels = new byte[writeImage.PixelWidth * writeImage.PixelHeight * 4];
                for (var h = 0; h < writeImage.PixelHeight; h++)
                    for (var w = 0; w < writeImage.PixelWidth; w++)
                    {
                        var pixel = writeImage.Pixels[index++];
                        var wIndex = w * 4 + h * writeImage.PixelWidth * 4;

                        pixels[wIndex++] = (byte)(pixel >> 16 & 0xFF);
                        pixels[wIndex++] = (byte)(pixel >> 8 & 0xFF);
                        pixels[wIndex++] = (byte)(pixel >> 0 & 0xFF);
                        pixels[wIndex++] = (byte)(pixel >> 24 & 0xFF);
                    }

                result = new Texture2D(graphicsDevice, writeImage.PixelWidth, writeImage.PixelHeight);
                result.SetData(pixels);
                Wait.Set();
            });

理论上一切正常。但不知何故,动作代码永远不会被调用。 我在非ui工作线程中调用BeginInvoke,Deployment.Current.Dispatcher不为null并提供ui-thread调度程序。 游戏是由MonoGame模板为VS2012制作的WP8项目。 我尝试在主线程(游戏构造函数)中获取并保存到静态变量调度程序和 SynchronizationContext 以确保它们完全属于 ui 线程,但这没有帮助。

我也试过这样的代码:

SynchronizationContext uiThread = Hub.SyncContext;//set in main thread (Game constructor)
var waitHandle = new object();
lock (waitHandle)
{
    uiThread.Post(_ => 
    {
        lock (waitHandle)
        {

            BitmapImage image = new BitmapImage();
            image.SetSource(stream);
            WriteableBitmap writeImage = new WriteableBitmap(image);

            var index = 0;
            var pixels = new byte[writeImage.PixelWidth * writeImage.PixelHeight * 4];
            for (var h = 0; h < writeImage.PixelHeight; h++)
                for (var w = 0; w < writeImage.PixelWidth; w++)
                {
                    var pixel = writeImage.Pixels[index++];
                    var wIndex = w * 4 + h * writeImage.PixelWidth * 4;

                    pixels[wIndex++] = (byte)(pixel >> 16 & 0xFF);
                    pixels[wIndex++] = (byte)(pixel >> 8 & 0xFF);
                    pixels[wIndex++] = (byte)(pixel >> 0 & 0xFF);
                    pixels[wIndex++] = (byte)(pixel >> 24 & 0xFF);
                }

            result = new Texture2D(graphicsDevice, writeImage.PixelWidth, writeImage.PixelHeight);
            result.SetData(pixels);

           Monitor.Pulse(waitHandle);
        }

    }, null);

    Monitor.Wait(waitHandle);
}

但还是没有运气:等待时执行停止,动作代码中的断点永远不会触发。

更新:我刚刚添加了带有一个日志的简单代码来示例 MonoGame 项目。 如果我等待 AutoResetEvent,我永远不会看到任何日志。当我删除 are.WaitOne() 动作开始工作。我已将其替换为 are.WaitOne(1000) 并开始按预期工作(为什么?)。 但是图像加载仍然没有运气。当我在 are.WaitOne() 中等待时,似乎有什么东西阻止了主线程运行。

【问题讨论】:

  • 如果您在 UI 线程(即加载或启动等)上获取 Deployment.Current.Dispatcher 值并存储它,这会有所不同吗?
  • @Tertium,你为什么不在这里简单地使用uiThread.Send,而不是使用PostMonitor.Pulse
  • @PeterRitchie 不,这没有帮助
  • @Noseratio 我认为问题更深,发送也不起作用
  • @Tertium,您发布的代码片段不足以进一步说明这一点。

标签: c# multithreading windows-phone-8 monogame begininvoke


【解决方案1】:

我通过使用这个库解决了问题: https://github.com/Taggersoft/ImageTools 这个 fork 完全适用于 WP8,如果你删除所有 Contract.Requires 类似的语句,它会非常好用。 这是我的 Texture2d.FromStream 替换:

        public static Texture2D TextureFromStream(GraphicsDevice graphicsDevice, Stream stream, String dbg_nm)
    {           
        #if WP8
        byte [] b = new byte[4];
        stream.Read(b, 0, 4);
        stream.Seek(0, SeekOrigin.Begin);
        int stream_type = getStreamType(b, 0);
        Texture2D result = null;

        if (stream_type == 1 || stream_type == 0)
        {
            var image = new ExtendedImage();
            if (stream_type == 0)
                new JpegDecoderNano().Decode(image, stream);
            else
            {
                try
                {
                    new PngDecoder().Decode(image, stream);
                }
                catch (System.Exception ex)
                {
                    Debug.WriteLine("Error reading " + dbg_nm + ":" + ex.Message);
                }                   
            }

            var Colors = new Color[image.Pixels.Length / 4];
            for (var i = 0; i < image.Pixels.Length / 4; i++)
            {
                Color unpackedColor = new Color();
                unpackedColor.R = (byte)(image.Pixels[i * 4 + 0]);
                unpackedColor.G = (byte)(image.Pixels[i * 4 + 1]);
                unpackedColor.B = (byte)(image.Pixels[i * 4 + 2]);
                unpackedColor.A = (byte)(image.Pixels[i * 4 + 3]);
                Colors[i] = unpackedColor;
            }
            Texture2D texture2D = new Texture2D(graphicsDevice, image.PixelWidth, image.PixelHeight);
            texture2D.SetData(Colors);
            result = texture2D;
        }

        return result;

        #else
        return Texture2D.FromStream(graphicsDevice,  stream);
        #endif
    }

在游戏构造函数中:

    #if WP8
    Decoders.AddDecoder<PngDecoder>();
    Decoders.AddDecoder<JpegDecoder>();
    #endif

这实际上是一种解决方法。但在我的情况下,这种方式比使用 uithread 中的 MS BitmapImage 要好得多。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多