如何将BitmapImage引用传递给BackgroundWorker.DoWork

本文关键字:BackgroundWorker DoWork 引用 BitmapImage | 更新日期: 2023-09-27 17:50:17

我有一个从资源流构造的BitmapImage。我需要将其发送到后台工作线程进行一些后处理。但似乎工作线程无法访问位图数据并获得系统。UnauthorizedAccessException在我的加载图像的第一行。如何解决这个问题?我还需要转移处理过的位图回UI (XAML)显示。如何正确地做到这一点?

    ImageLoader.RunWorkerAsync(albumArtImage);
    private void LoadImage(object sender, DoWorkEventArgs e)
    {
        WriteableBitmap wb = new WriteableBitmap((BitmapImage)e.Argument);
        wb.Resize(AppWidth, AppHeight, WriteableBitmapExtensions.Interpolation.Bilinear);
        var wb2 = WriteableBitmapExtensions.Convolute(wb, WriteableBitmapExtensions.KernelGaussianBlur3x3);            
        e.Result = wb2;
    }
    private void LoadImageCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        AlbumBackground.ImageSource = (WriteableBitmap)e.Result;
    }

如何将BitmapImage引用传递给BackgroundWorker.DoWork

WriteableBitmap需要从UI线程调用。这只是那个实用程序的一个限制,你无法绕过它。

从MSDN:

WriteableBitmap类使用两个缓冲区。回缓冲区在系统内存中分配,并累积当前未显示的内容。前缓冲区在系统内存中分配,包含当前显示的内容。呈现系统将前缓冲区复制到显存中以供显示。

两个线程使用这些缓冲区。用户界面(UI)线程生成UI,但不将其呈现在屏幕上。UI线程响应用户输入、计时器和其他事件。一个应用程序可以有多个UI线程。呈现线程合成并呈现来自UI线程的更改。每个应用程序只有一个渲染线程。

UI线程将内容写入回缓冲区。渲染线程从前端缓冲区读取内容并将其复制到显存。对后缓冲区的更改将随着矩形区域的更改而跟踪。

http://msdn.microsoft.com/en-us/library/system.windows.media.imaging.writeablebitmap.aspx

BackgroundWorker的替代选项:

无法回避需要在UI线程上执行此操作的事实。但是您可以将它推到队列的末尾,这样用户就不会注意到它了。

试试这样写:

public static void RunDelayedOnUiThread(Action action)
{
     Deployment.Current.Dispatcher.BeginInvoke(() =>
     {
         var timer = new DispatcherTimer 
                     { 
                         Interval = TimeSpan.FromMilliseconds(1) 
                     };
         timer.Tick += (sender, args) =>
         {
             timer.Stop();
             action();
         };
         timer.Start();
     });
}

这应该足以使您的图像处理(在本例中传递的操作)将放在等待UI线程处理的队列的末尾。当UI线程工作时,图像处理仍然会阻塞它,但至少应该先完成其他所有事情。

工作原理:

当你调用BeginInvoke时,你的动作被放置在调度程序队列的末尾,当UI线程从它当前正在做的事情(运行你的代码)中释放出来时运行。但是因为你可以再次调用BeginInvoke(一次又一次)然后你的图像处理会阻塞dispatcher队列中的其他东西,我们想要再次把它放在后面。这就是DispatcherTimer发挥作用的地方。从MSDN:

定时器不保证正好在时间间隔发生的时候执行,但可以保证在时间间隔发生之前不执行。这是因为DispatcherTimer操作像其他操作一样放在Dispatcher队列上。DispatcherTimer操作何时执行取决于队列中的其他作业及其优先级。

所以我们给它的一毫秒间隔应该足以把这个推回到队列的末尾。

在Windows Phone 7上,这可能仍然会阻止一些触摸事件,但至少你不会阻止页面渲染。

在Windows Phone 8上,全景、枢轴和LongListSelector都在非UI线程上响应输入,所以你在那里会更安全。

我不同意位图修改只是一个UI过程。我已经实现了一些代码(可能太长,不能发布),计算修改参数为单独的后台工作中的单独位图,在一些数组中存储数据(用于像素分配),当计算完成并设置一些布尔值作为"while"answers"if"开关。在另一个BGW中,每个要处理的位图对应一个BGW,我有一个等待计算完成布尔值触发的循环。当触发时,次要工作程序从数组中复制结果,然后重新启动计算线程开始下一帧的计算…(我有没有提到这是一部动画??)这些BGWs然后继续使用tempbit.SetPixel(x,y,color)//将复制的参数应用于临时位图(每个原始位图对应一个)。一旦位图被更新,就会抛出更多的布尔开关(与此同时,计算bgw会在下一帧上运行)。然后在最后一个BGW上,我实现了等待每个单独的临时位图更新的代码。当他们这样做时,我复制位图,并重新启动图像更新bgw(这个布尔结构防止任何对象重叠)。复制完所有图像后,我将合并这些图像,然后更新一个图片框。Image然后重新启动循环(这将需要一些布尔逻辑和goto语句或restart异步中继方法)。所以基本上,只要不允许两个后台工作者对象访问共享的第三个对象,就不会抛出异常。

通俗地说,我为每个单独的任务都有单独的bgw,并实现逻辑以防止任何线程重叠。这仍然允许BGW类的异步优势,没有所有的跨线程和繁忙的检查的麻烦。

编码快乐!

这对我很有效。在将位图传递给backgroundWorker之前,我冻结了位图,没有抛出异常。

BackgroundWorker bw = new BackgroundWorker();
BitmapSource bmpCanvas;
BitmapSource bmpRef;
List<object> arg = new List<object>();
arg.Add(bmpCanvas);
arg.Add(bmpRef);
bmpCanvas.Freeze();
bmpRef.Freeze();
bw.RunWorkerAsync(arg);

当我在backgroundworker中完成位图时,我会冻结位图。

void bw_DoWork(object sender, DoWorkEventArgs e)
{
   List<object> argu = e.Argument as List<object>;
   BitmapSource bCanvas = (BitmapSource)argu[0];
   BitmapSource bref = (BitmapSource)argu[1];
   // doing something with those images...
   bCanvas.Freeze();
   e.Result = bCanvas;
}

和i更新位图图像在RunWorkerCompleted.

void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
   BitmapSource bmpCanvas = (BitmapSource)e.Result;
   image.Source = bmpCanvas;
}
http://msdn.microsoft.com/en-us/library/ms750509.aspx

相关文章:
  • 没有找到相关文章