任何人都可以帮我解决这个问题......
我正在尝试使用多线程创建一个窗口来处理图像。
如果我为每个处理操作启动process.exe
,我就不会遇到这个问题....
这是我的功能:
private ImageSource ImageApplyEffect(ImageSource imageSource, Effect effect, Size size)
{
DrawingVisual d = new DrawingVisual();
DrawingContext dc = d.RenderOpen();
dc.DrawImage(imageSource, new Rect(new Point(0, 0), size));
d.Effect = effect;
dc.Close();
dc = null;
RenderTargetBitmap frameRenderTargetBitmap2 = new RenderTargetBitmap((int)size.Width,
(int)size.Height,
1 / 96,
1 / 96,
PixelFormats.Default);
frameRenderTargetBitmap2.Render(d);
d = null;
BitmapFrame frameBitmapFrame2 = BitmapFrame.Create(frameRenderTargetBitmap2);
frameRenderTargetBitmap2 = null;
MemoryStream mStream = new MemoryStream();
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(frameBitmapFrame2);
encoder.Save(mStream);
BitmapImage bImg = new BitmapImage();
bImg.BeginInit();
bImg.StreamSource = mStream;
bImg.EndInit();
frameBitmapFrame2 = null;
ImageSource imgS = bImg;
encoder = null;
return imgS;
}
我得到了这个例外:
调用线程无法访问此对象,因为另一个线程拥有它!
例外:
em System.Windows.DependencyObject.GetValue(DependencyProperty dp)
EM System.Windows.Media.Effects.PixelShader.ManualUpdateResource(通道 channel,Boolean skipOnChannelCheck)em System.Windows.Media.Effects.PixelShader.UpdateResource(通道 channel,Boolean skipOnChannelCheck)em System.Windows.Media.Effects.PixelShader.System.Windows.Media.Composition.DUCE.IResource.AddRefOnChannel(通道 频道)em System.Windows.Media.Effects.ShaderEffect.AddRefOnChannelCore(通道 频道)em System.Windows.Media.Effects.Effect.System.Windows.Media.Composition.DUCE.IResource.AddRefOnChannel(通道 频道)em System.Windows.Media.Visual.UpdateEffect(频道 channel,ResourceHandle句柄,VisualProxyFlags标志,布尔值 isOnChannel)em System.Windows.Media.Visual.RenderRecursive(RenderContext ctx)em System.Windows.Media.Visual.Render(RenderContext ctx,UInt32 childIndex)em System.Windows.Media.Renderer.Render(IntPtr pRenderTarget,Channel channel,Visual visual,Int32 width,Int32 高度,双dpiX,双dpiY,矩阵worldTransform,Rect windowClip)em System.Windows.Media.Imaging.BitmapVisualManager.Render(Visual visual, Matrix worldTransform,Rect windowClip)em System.Windows.Media.Imaging.BitmapVisualManager.Render(Visual visual)
答案 0 :(得分:0)
这里的技巧是冻结位图图像并将其传递回UI线程。此外,某些对象需要序列化/反序列化,以便您可以将它们传递给新线程
public class Serializer
{
public static Stream Serialize<T>(T t)
{
var memoryStream = new MemoryStream();
var serializer = new BinaryFormatter();
serializer.Serialize(memoryStream, t);
return memoryStream;
}
public static T Deserialize<T>(Stream stream)
{
var formatter = new BinaryFormatter();
stream.Seek(0, SeekOrigin.Begin);
return (T)formatter.Deserialize(stream);
}
}
public Task<BitmapImage> ApplyImageEffect(ImageSource imageSource, Effect effect, Size size)
{
var imgSourceSerialized = Serializer.Serialize(imageSource);
var effectSerialized = Serializer.Serialize(effect);
return Task.Factory.StartNew(() =>
{
var imgSourceDeserialized = Serializer.Deserialize<ImageSource>(imgSourceSerialized);
var effectDeserialized = Serializer.Deserialize<Effect>(effectSerialized);
var drawingVisual = new DrawingVisual();
var drawingContext = drawingVisual.RenderOpen();
drawingContext.DrawImage(imgSourceDeserialized, new Rect(new Point(0, 0), size));
drawingVisual.Effect = effectDeserialized;
drawingContext.Close();
var frameRenderTargetBitmap2 = new RenderTargetBitmap((int)size.Width,
(int)size.Height,
1 / 96,
1 / 96,
PixelFormats.Default);
frameRenderTargetBitmap2.Render(drawingVisual);
var frameBitmapFrame2 = BitmapFrame.Create(frameRenderTargetBitmap2);
var memoryStream = new MemoryStream();
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(frameBitmapFrame2);
encoder.Save(memoryStream);
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = memoryStream;
bitmapImage.EndInit();
// important!
bitmapImage.Freeze();
return bitmapImage;
});
}
public MainWindow()
{
InitializeComponent();
Task<BitmapImage> [] tasks = new[]
{
ApplyImageEffect(new BitmapImage(), new BlurEffect(), new Size(20, 20)),
ApplyImageEffect(new BitmapImage(), new BlurEffect(), new Size(20, 20))
};
Task.WaitAll(tasks);
}
如果它不起作用,那么你需要用“new Thread()”构造替换Task.Factory.StartNew并将公寓状态设置为STA。
我曾写过一些旧代码,这种代码证明了这一概念:
/// <summary>
/// Creates UI element on a seperate thread and transfers it to
/// main UI thread.
///
/// Usage; if you have complex UI operation that takes a lot of time, such as XPS object creation.
/// </summary>
/// <param name="constructObject">Function that creates the necessary UIElement - will be executed on new thread</param>
/// <param name="constructionCompleted">Callback to the function that receives the constructed object.</param>
public void CreateElementOnSeperateThread(Func<UIElement> constructObject, Action<UIElement> constructionCompleted)
{
VerifyAccess();
// save dispatchers for future usage.
// we create new element on a seperate STA thread
// and then basically swap UIELEMENT's Dispatcher.
Dispatcher threadDispatcher = null;
var currentDispatcher = Dispatcher.CurrentDispatcher;
var ev = new AutoResetEvent(false);
var thread = new Thread(() =>
{
threadDispatcher = Dispatcher.CurrentDispatcher;
ev.Set();
Dispatcher.Run();
});
thread.SetApartmentState(ApartmentState.STA);
thread.IsBackground = true;
thread.Start();
ev.WaitOne();
threadDispatcher.BeginInvoke(new Action(() =>
{
var constructedObject = constructObject();
currentDispatcher.BeginInvoke(new Action(() =>
{
var fieldinfo = typeof (DispatcherObject).GetField("_dispatcher",
BindingFlags.NonPublic |
BindingFlags.Instance);
if (fieldinfo != null)
fieldinfo.SetValue(constructedObject, currentDispatcher);
constructionCompleted(constructedObject);
threadDispatcher.BeginInvokeShutdown(DispatcherPriority.Normal);
}), DispatcherPriority.Normal);
}), DispatcherPriority.Normal);
}