如何在引发自定义事件时修复跨线程异常? [英] How to fix cross thread exception when raising a custom event?

查看:77
本文介绍了如何在引发自定义事件时修复跨线程异常?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我创建了一个名为HttpDownloader的dll。它有三个事件。 ProgressChanged,DownloadCompleted和DownloadCancelled。当我使用ProgressChanged事件将进度写入Form.cs中的listview时,它会抛出一个InvalidOperationException,在其短信中写入不允许跨线程操作。



如何在不调用Form.cs中的控件的情况下解决此问题。我复制了我的代码部分,我认为有必要在下面进行说明。



I created a dll named HttpDownloader. It has three events. ProgressChanged,DownloadCompleted and DownloadCancelled. When I use the ProgressChanged event to write the progress to a listview in Form.cs it throws an InvalidOperationException which "cross thread operations not allowed" is written in its text message.

How can I fix this without invoking control in Form.cs . I copied my code parts that I think it's necessary to be understanded below.

void Download(int startRange, bool overWrite)
{
//some code lines....
while ((bytesRead = str.Read(buffer, 0, buffer.Length)) > 0)
            {
                if (state == DownloadState.Cancelled | state == DownloadState.Paused) break;
                state = DownloadState.Downloading;
                file.Write(buffer, 0, bytesRead);
                file.Flush();
                bytesReceived += bytesRead;
                speedBytes += bytesRead;
                this.Progress = progress = (int)(bytesReceived * 100.0 / contentLength);
                speed = (int)(speedBytes / 1.0 / stpWatch.Elapsed.TotalSeconds);

            }
//some code lines....
}

public int Progress
        {
            get { return progress; }
            private set
            {
                progress = value;
                if (DownloadProgressChanged != null)
                    DownloadProgressChanged.Invoke(this, new DownloadProgressEventArgs(progress, speed));
            }
        }
public async void StartDownloadAsync()
        {
            if (state != DownloadState.Started & state != DownloadState.Completed & state != DownloadState.Cancelled)
                return;

            state = DownloadState.Started;
            await Task.Run(() =>
            {
                Download(0, false);
            });

        }
void downloader_QueueProgressChanged(object sender, EventArgs e)
        {
            listViewDwlds.Items[0].Text = downloader.CurrentProgress + "%";
        }

{

推荐答案

如果事件是自定义的,则无关紧要。您不能直接从非UI线程使用UI元素的方法和属性。相反,您可以使用UI线程调用机制。您需要使用方法调用 BeginInvoke System.Windows.Threading.Dispatcher (对于Forms或WPF)或 System.Windows.Forms.Control (仅限表格)。



您将在我过去的答案中找到有关其工作原理和代码示例的详细说明:

主线程上的.NET事件 [ ^ ],

Control.Invoke()vs。Control.BeginInvoke()

Treeview扫描仪和MD5的问题



见还有更多关于线程的参考:

主线程上的.NET事件

如何让keydown事件在vb.net中的不同线程上运行

在启用后控制事件未触发禁用+多线程



-SA
It does not matter if event is custom or not. You cannot directly use methods and properties of the elements of the UI from a non-UI thread. Instead, you can use UI thread invocation mechanism. You need to use the method Invoke or BeginInvoke of System.Windows.Threading.Dispatcher (for both Forms or WPF) or System.Windows.Forms.Control (Forms only).

You will find detailed explanation of how it works and code samples in my past answers:
.NET event on main thread[^],
Control.Invoke() vs. Control.BeginInvoke(),
Problem with Treeview Scanner And MD5.

See also more references on threading:
.NET event on main thread,
How to get a keydown event to operate on a different thread in vb.net,
Control events not firing after enable disable + multithreading.

—SA


我刚刚找到了一个更好的解决方案,使用 AsyncOperationManager 类。代码示例如下。





I have just found a better solution for this problem using AsyncOperationManager class. A code sample is below.


public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }
    private void button1_Click(object sender, EventArgs e)
    {
        Counter myCounter = new Counter(1000);
        myCounter.IndexValueChanged += myCounter_IndexValueChanged;
        myCounter.StartCountAsync();
    }
    void myCounter_IndexValueChanged(object sender, IndexValueChangedEventArgs e)
    {
        textBox1.Text = e.Index.ToString();
    }
}
class Counter
{
    public delegate void IndexValueChangedEventHandler(object sender, IndexValueChangedEventArgs e);
    public event IndexValueChangedEventHandler IndexValueChanged;

    int _maxNumber;
    int _index;

    public Counter(int maxNumber)
    {
        _maxNumber = maxNumber;
    }

    public async void StartCountAsync()
    {
        //We create an instance of AsyncOperation class
        AsyncOperation asyncCountOperation = AsyncOperationManager.CreateOperation(null);

        await Task.Run(() =>
        {
            for (int i = 0; i < _maxNumber; i++)
            {
                _index = i;
                //We use post method with method will be invoked
                asyncCountOperation.Post(new SendOrPostCallback(delegate
                    {
                        if (IndexValueChanged != null)
                            IndexValueChanged(this,
                                           new IndexValueChangedEventArgs(_index));
                    }), null);
                /*If we raise event without Post method like below it throws an error because of cross thread calls
                 * if (IndexValueChanged != null)
                            IndexValueChanged(this,
                                           new IndexValueChangedEventArgs(_index));
                */
            }
        });
    }
}
class IndexValueChangedEventArgs
{
    int indexNum;
    public IndexValueChangedEventArgs(int index)
    {
        indexNum = index;
    }
    public int Index
    {
        get { return indexNum; }
    }
}


这篇关于如何在引发自定义事件时修复跨线程异常?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆