0

我有一个从资源流构造的 BitmapImage。我需要将它发送到后台工作线程进行一些后期处理。但似乎工作线程无法访问位图数据并在我的 LoadImage 第一行中获取System.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;
    }
4

3 回答 3

1

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(一次又一次),然后您的图像处理会阻塞调度程序队列中的其他内容,所以我们想再次将其放在后面。这就是 DispatcherTimer 的用武之地。来自 MSDN:

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

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

在 Windows Phone 7 上,这可能仍会阻止某些触摸事件,但至少您不会阻止页面呈现。

在 Windows Phone 8 上,Panorama、Pivo​​t 和 LongListSelector 都响应非 UI 线程上的输入,因此您在那里会更安全一些。

于 2012-12-31T12:48:25.520 回答
0

这对我有用。我在将位图传递给 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;
}

我更新了 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

于 2013-01-29T01:43:41.190 回答
0

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

用外行的话来说,我有单独的 BGW 来处理单独的任务,并实现逻辑以防止任何线程重叠。这仍然允许 BGW 类的异步优势,而没有跨线程和忙检查的大惊小怪。

快乐编码!

于 2013-08-18T08:48:56.073 回答