繁体   English   中英

WPF中的多线程使用C#(与后台工作者)

[英]Multi threading in WPF using C# (with background worker)

我编写了代码来保存应用程序生成的图像。 图像大小约为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 Forms中编程时,类似的技巧也奏效了。)

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

这个怎么做? 在WPF中有多种其他简单的多线程方法吗?

创建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);
            }
        }
    )
);

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

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

bw.RunWorkerAsync(cache);

并从DoWork方法获取它:

var cache=(CacheType) e.Argument;

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM