.NET中的BeginInvoke问题

时间:2010-01-17 14:14:11

标签: .net asynchronous youtube-api begininvoke

我有以下代码向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);
}

错误

  

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

4 个答案:

答案 0 :(得分:4)

您正在调用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; });

答案 1 :(得分:1)

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

答案 2 :(得分:0)

由于对Forms控件的访问本质上不是线程安全的,因此调试器会通过从其他线程访问规则来警告您违反规则。相反,您可以直接Invoke控件来获得所需的结果。有一个很棒的,全面的Microsoft教程,介绍如何执行此操作 here

答案 3 :(得分:0)

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

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

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