事件中控件文本更新抛出非法跨线程异常

Control text update in event throws illegal cross thread exception

我正在以编程方式通过关键字进行 Youtube 搜索,并使用 Youtube API 来执行此操作。我想在搜索进度完成并且 return YoutubeSearchCompletedEventArgs 中的结果由 YoutubeSearchCompleted 发送时触发一个事件。

但是Form.csYoutubeSearchCompleted中的代码抛出跨线程非法操作异常。通常,使用 AsyncOperation.Post 方法它不能抛出 InvalidOperationException。因为我之前在一个下载管理器项目中用过同样的方法,效果很好。所以我不明白为什么会这样。

Youtube search class

class YouTubeManager
{
    public delegate void YoutubeSearchCompletedEventHandler(object sender, YoutubeSearchCompletedEventArgs e);
    public event YoutubeSearchCompletedEventHandler YoutubeSearchCompleted;
    AsyncOperation aop = AsyncOperationManager.CreateOperation(null);

    List<YoutubeVideo> SearchByKeyword(string keyword)
    {
        List<YoutubeVideo> videos = new List<YoutubeVideo>();

        //.......
        //...Youtube data api search codes....
        //.......

        return videos;
    }
    public void Search(string keyword)
    {
        Task.Run(() =>
        {
            List<YoutubeVideo> list = SearchByKeyword(keyword);
            aop.Post(new System.Threading.SendOrPostCallback(delegate
            {
                if (YoutubeSearchCompleted != null)
                    YoutubeSearchCompleted(this, 
                        new YoutubeSearchCompletedEventArgs(keyword, list);
            }), null);
        });
    }
}

Form.cs

public partial class Form1 : Form
{
    YouTubeManager yam = new YouTubeManager();
    public Form1()
    {
        InitializeComponent();
        this.Load += Form1_Load;
    }

    void Form1_Load(object sender, EventArgs e)
    {
        yam.YoutubeSearchCompleted += yam_YoutubeSearchCompleted;
        yam.Search("Blues");
    }

    void yam_YoutubeSearchCompleted(object sender, YoutubeSearchCompletedEventArgs e)
    {
        if (e.Videos.Count < 1) return;

        textBox1.Text = e.Videos[0].Title();
    }
}

在此代码中,textBox1.Text = e.Videos[0].Title(); 行抛出 InvalidOperationException。我该如何解决这个问题?

注:我不要Invoke方法,只需要AsyncOperation.

问题很可能是 AsyncOperation 创建得太早造成的。您可以通过以下方式检查:

if (!(aop.SynchronizationContext is WindowsFormsSynchronizationContext))
{
    // Oops - we have an issue
}

这是为什么? AsyncOperation在构造时存储SynchronizationContext.Current,通常所有Control派生的classes(包括Form)从里面安装WindowsFormsSynchronizationContext Control class 构造函数。

但假设 Forgm1 是您的启动表单(例如来自 Main 的典型 Application.Run(new Form1()); 调用)。由于 Any instance variable initializers in the derived class are executed before the base class constructor,在 aop 变量被初始化时(通过 yam 字段初始化器),Control class 构造函数尚未 运行 ,因此 WindowsFormsSynchronizationContext 未安装,因此 AsynOperation 使用默认 SynchronozationContext 初始化,它通过在单独的线程上简单地执行它来实现 Post

修复很简单 - 不要使用初始化程序,只需定义字段

YouTubeManager yam;

并移动初始化

yam = new YouTubeManager();

在表单构造函数或加载事件中。