WPF中的multithreading使用C#(与后台工作者)

我编写了代码来保存应用程序生成的图像。 图像的大小约为32-35 MB。 将图像保存为BMB文件时,需要很长时间,大约3-5秒。 为此,我使用了后台工作程序但是在运行后台工作程序时,它显示了一个错误,例如……“无法访问在不同线程上创建的对象”。

以下是代码:

private void btnSaveDesign_Click(object sender, RoutedEventArgs e) { Microsoft.Win32.SaveFileDialog sfd = new Microsoft.Win32.SaveFileDialog(); sfd.Title = "Save design as..."; sfd.Filter = "BMP|*.bmp"; if (sfd.ShowDialog() == true) { ww = new winWait(); ww.Show(); System.ComponentModel.BackgroundWorker bw = new System.ComponentModel.BackgroundWorker(); bw.DoWork += new System.ComponentModel.DoWorkEventHandler(bw_DoWork); bw.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(bw_RunWorkerCompleted); fName = sfd.FileName; cache = new CachedBitmap((BitmapSource)imgOut.Source, BitmapCreateOptions.None, BitmapCacheOption.OnLoad); bw.RunWorkerAsync(); } } void bw_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e) { ww.Close(); } void bw_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) { BmpBitmapEncoder encoder = new BmpBitmapEncoder(); encoder.Frames.Add(BitmapFrame.Create(cache)); //here... it says cant access... using (FileStream file = File.OpenWrite(fName)) { encoder.Save(file); } } 

我已将“缓存”声明为全局对象。 (当我使用VB.NET在Windows窗体中编程时,类似的技巧也奏效了。)

ww是我想在执行进程时显示的等待窗口。

这该怎么做? 在WPF中有多种其他简单的multithreading方法吗?

创建WPF对象时,会将它们分配给Dispatcher对象。 这不允许创建线程以外的任何线程访问该对象。 通过调用冻结方法冻结对象可以避免这种情况。 您需要在bitmapsource对象上调用Freeze。 冻结对象后,它就变得无法使用了

您的问题是因为您正在访问不是由后台工作线程创建的对象。 通常,如果您访问在主线程中创建并从不同线程访问的UI控件,则会发生这种情况。

请使用以下代码。

 Dispatcher.Invoke ( new Action( delegate() { BmpBitmapEncoder encoder = new BmpBitmapEncoder(); encoder.Frames.Add(BitmapFrame.Create(cache)); using (FileStream file = File.OpenWrite(fName)) { encoder.Save(file); } } ) ); 

我认为你必须将缓存作为参数传递给新线程:

 bw.RunWorkerAsync(cache); 

并从DoWork方法获取它:

 var cache=(CacheType) e.Argument; 

.NET框架提供了一种使用BackgroundWorker组件进行线程入门的简单方法。 这包含了大部分的复杂性,并使得生成后台线程相对安全。 此外,它允许您在后台线程和UI线程之间进行通信,而无需进行任何特殊编码。 您可以将此组件与WinForms和WPF应用程序一起使用。 BackgroundWorker提供了一些function,包括生成后台线程,在完成后取消后台进程的function,以及将进度报告回UI的机会。

 public BackgroudWorker() { InitializeComponent(); backgroundWorker = ((BackgroundWorker)this.FindResource("backgroundWorker")); } private int DoSlowProcess(int iterations, BackgroundWorker worker, DoWorkEventArgs e) { int result = 0; for (int i = 0; i <= iterations; i++) { if (worker != null) { if (worker.CancellationPending) { e.Cancel = true; return result; } if (worker.WorkerReportsProgress) { int percentComplete = (int)((float)i / (float)iterations * 100); worker.ReportProgress(percentComplete); } } Thread.Sleep(100); result = i; } return result; } private void startButton_Click(object sender, RoutedEventArgs e) { int iterations = 0; if (int.TryParse(inputBox.Text, out iterations)) { backgroundWorker.RunWorkerAsync(iterations); startButton.IsEnabled = false; cancelButton.IsEnabled = true; outputBox.Text = ""; } } private void cancelButton_Click(object sender, RoutedEventArgs e) { // TODO: Implement Cancel process this.backgroundWorker.CancelAsync(); } private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e) { // e.Result = DoSlowProcess((int)e.Argument); var bgw = sender as BackgroundWorker; e.Result = DoSlowProcess((int)e.Argument, bgw, e); } private void BackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) { workerProgress.Value = e.ProgressPercentage; } private void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { if (e.Error != null) { MessageBox.Show(e.Error.Message); } else if (e.Cancelled) { outputBox.Text = "Canceled"; workerProgress.Value = 0; } else { outputBox.Text = e.Result.ToString(); workerProgress.Value = 0; } startButton.IsEnabled = true; cancelButton.IsEnabled = false; }