简体   繁体   English

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

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

I have written code to save an image which is generated by the application. 我编写了代码来保存应用程序生成的图像。 The size of the image is around 32-35 MB. 图像大小约为32-35 MB。 While saving the image to a BMB file, it is taking a long time, around 3-5 secs. 将图像保存为BMB文件时,需要很长时间,大约3-5秒。 For this purpose, I have used a background worker but when running the background worker, it shows an error like..."can't access the object as it is created on different thread". 为此,我使用了后台工作程序但是在运行后台工作程序时,它显示了一个错误,例如......“无法访问该对象,因为它是在不同的线程上创建的”。

Following is the code: 以下是代码:

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

I have declared "cache" as a global object. 我已将“缓存”声明为全局对象。 (A similar trick worked when I was programming in Windows Forms with VB.NET.) (当我使用VB.NET在Windows Forms中编程时,类似的技巧也奏效了。)

ww is the wait window that I want to be displayed while the precess is being executed. ww是我想在执行进程时显示的等待窗口。

How to do this? 这个怎么做? Is there any other simple method for multi threading in WPF? 在WPF中有多种其他简单的多线程方法吗?

When WPF objects are created they are assigned to a Dispatcher object. 创建WPF对象时,会将它们分配给Dispatcher对象。 This disallows any threads other than the creating thread to access the object. 这不允许创建线程以外的任何线程访问该对象。 This can be circumvented by freezing the object by calling the freeze method. 通过调用冻结方法冻结对象可以避免这种情况。 You would need to call Freeze on your bitmapsource object. 您需要在bitmapsource对象上调用Freeze。 Once you have frozen your object it becomes uneditable 冻结对象后,它就变得无法使用了

Your problem comes about because you are accessing an object which is not created by the background worker thread. 您的问题是因为您正在访问不是由后台工作线程创建的对象。 Normally this would happen if you access a UI control which is created in the main thread and accessed from different thread. 通常,如果您访问在主线程中创建并从不同线程访问的UI控件,则会发生这种情况。

Use the code below. 请使用以下代码。

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 framework provides a simple way to get started in threading with the BackgroundWorker component. .NET框架提供了一种使用BackgroundWorker组件进行线程入门的简单方法。 This wraps much of the complexity and makes spawning a background thread relatively safe. 这包含了大部分的复杂性,使得生成后台线程相对安全。 In addition, it allows you to communicate between your background thread and your UI thread without doing any special coding. 此外,它允许您在后台线程和UI线程之间进行通信,而无需进行任何特殊编码。 You can use this component with WinForms and WPF applications. 您可以将此组件与WinForms和WPF应用程序一起使用。 The BackgroundWorker offers several features which include spawning a background thread, the ability to cancel the background process before it has completed, and the chance to report the progress back to your UI. 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;
            }

I think you have to pass cache as a parameter to the new thread: 我认为你必须将缓存作为参数传递给新线程:

bw.RunWorkerAsync(cache);

and get it from the DoWork method: 并从DoWork方法获取它:

var cache=(CacheType) e.Argument;

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

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