【问题标题】:Problem with BeginInvoke in .NET.NET 中的 BeginInvoke 问题
【发布时间】:2011-01-06 01:51:46
【问题描述】:

我有以下代码向 youtube 发送查询并将总结果发送到文本框。如果我只是提醒结果,没关系,但我不能将结果分配给文本框。请解释一下为什么?

private void SearchVideo(string keyword)
{
    string orderBy = "";
    switch (cboSortBy.SelectedIndex)
    {
        case 1: orderBy = "published"; break;
        case 2: orderBy = "viewCount"; break;
        case 3: orderBy = "rating"; break;
        default: orderBy = "relevance"; break;
    }
    SearchDelegate sd = Search;
    sd.BeginInvoke(keyword, orderBy, SearchCompleted, sd);
}

private void SearchCompleted(IAsyncResult ar)
{
    if (null == ar) return;
    SearchDelegate sd = ar.AsyncState as SearchDelegate;
    Feed<Video> result = sd.EndInvoke(ar);
    txtSearch.Text = result.TotalResults.ToString();
}

private Feed<Video> Search(string keyword, string orderBy)
{
    YouTubeQuery query = new YouTubeQuery(YouTubeQuery.DefaultVideoUri);
    query.OrderBy = orderBy;
    query.Query = keyword;
    query.SafeSearch = YouTubeQuery.SafeSearchValues.None;
    return GetRequest().Get<Video>(query);
}

还有错误

跨线程操作无效: 控制 'txtSearch' 从 线程以外的线程 创建于。

【问题讨论】:

    标签: .net asynchronous youtube-api begininvoke


    【解决方案1】:

    您正在调用BeginInvoke,因此您的委托在线程池线程上被调用。您无法从该线程池线程访问 UI;您需要在控件上调用InvokeBeginInvoke,然后在UI 线程上使用结果。例如,使用匿名方法:

    txtSearch.BeginInvoke((MethodInvoker) delegate() 
        { txtSearch.Text = result.TotalResults.ToString(); }
    );
    

    或者使用 lambda 表达式,并且为了清楚起见使用单独的局部变量:

    MethodInvoker action= () => { txtSearch.Text = result.TotalResults.ToString();};
    txtSearch.BeginInvoke(action);
    

    使用Invoke 将使调用线程阻塞,直到 UI 线程调用委托; BeginInvoke 是非阻塞的。

    编辑:如果问题是 result.TotalResults 是需要很长时间的位,请在后台线程上继续执行该位:

    string text = result.TotalResults.ToString();
    txtSearch.BeginInvoke((MethodInvoker) delegate() { txtSearch.Text = text; });
    

    【讨论】:

    • BeginInvoke 仍然会阻止 GUI :(
    • @A New Chicken:你到底是什么意思? BeginInvoke 在委托和控件上调用它都是非阻塞的。以我所描述的方式使用 BeginInvoke 工作,并且不会阻塞 UI。如果您遇到问题,请提供一个简短但完整的程序来演示该问题。
    • 这是我在 C# 2008EE mediafire.com/?n220ddoi2i2 中的项目,请帮我检查一下,谢谢。
    • 非常感谢您的建议:D
    【解决方案2】:

    您可以考虑使用BackgroundWorker,而不是Delegate.BeginInvoke。 BackgroundWorker 在完成后引发RunWorkerCompleted 事件,该事件在 UI 线程中运行,因此您可以在那里更新您的用户界面。

    【讨论】:

    • 我已经尝试过 BackgroundWorker 但它仍然会阻止 UI :(
    • 这很奇怪;我已经在许多阻塞 UI 的项目中成功使用了 BackgroundWorker。你确定你用对了吗?在您的事件处理程序中:只需启动 BackgroundWorker(仅此而已)。在 BW.DoWork:做这项工作。在 BW.RunWorkerCompleted 中:更新 UI。
    • 访问属性TotalResults 是否可能实际执行所有工作?在这种情况下,您应该将该属性的访问移至后台线程,并将结果字符串传递给 UI 线程。
    • 我不这么认为。唯一不影响UI响应的方法是我贴的那个,但它是非线程安全的,微软的方法:msdn.microsoft.com/en-us/library/ms171728%28VS.80%29.aspx仍然没有解决问题>.>
    • 你能像 Jon 建议的那样尝试在后台移动 TotalResults 吗?对于您的 UI 线程阻塞的原因,这确实是唯一合理的解释......
    【解决方案3】:

    由于对表单控件的访问本质上不是线程安全的,因此调试器会警告您从不同的线程访问它会违反规则。相反,您可以直接Invoke 控件来获得您想要的结果。有一个很棒的、全面的 Microsoft 教程来说明如何做到这一点here

    【讨论】:

    • 使用线程安全的方法比正常的耗时长:(
    【解决方案4】:

    错误消息准确地告诉您问题所在。您不能在与创建控件的线程不同的线程上安全地操作 UI 控件;调试器旨在捕获这一点(有关详细信息,请参阅MSDN)。

    因此,您要么需要在控件上调用BeginInvoke 以便它在 UI 线程上执行,要么您需要在调用的线程和 UI 线程之间建立某种通信机制。显然前者可以简单地用TextBox.BeginInvoke来完成:

    txtSearch.BeginInvoke(sd, new object[] { keyword, orderBy, SearchCompleted });
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多