Проблема с BeginInvoke в .NET - PullRequest
1 голос
/ 17 января 2010

У меня есть следующий код, чтобы отправить запрос на 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' доступен из потока, отличного от потока, в котором он создан.

Ответы [ 4 ]

4 голосов
/ 17 января 2010

Вы звоните BeginInvoke, поэтому ваш делегат вызывается в потоке пула потоков. Вы не можете получить доступ к интерфейсу из этого потока пула потоков; вам нужно вызвать Invoke или BeginInvoke на элементе управления, чтобы затем использовать результаты в потоке пользовательского интерфейса. Например, используя анонимный метод:

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

Или с помощью лямбда-выражения и с отдельной локальной переменной просто для ясности:

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

Использование Invoke сделает блок вызывающего потока, пока поток пользовательского интерфейса не вызовет делегата; BeginInvoke неблокирует.

РЕДАКТИРОВАТЬ: Если проблема в том, что result.TotalResults это бит, который занимает много времени, сделайте этот бит все еще в фоновом потоке:

string text = result.TotalResults.ToString();
txtSearch.BeginInvoke((MethodInvoker) delegate() { txtSearch.Text = text; });
1 голос
/ 17 января 2010

Вместо Delegate.BeginInvoke вы можете рассмотреть возможность использования BackgroundWorker . BackgroundWorker вызывает событие RunWorkerCompleted после его завершения, которое запускается в потоке пользовательского интерфейса , поэтому вы можете обновить там свой пользовательский интерфейс.

0 голосов
/ 17 января 2010

Сообщение об ошибке говорит вам, в чем именно заключается проблема. Вы не можете безопасно манипулировать элементами управления пользовательского интерфейса в потоке, отличном от того, который создал элемент управления; отладчик предназначен для этого (подробнее см. MSDN ).

Итак, вам нужно либо вызвать BeginInvoke для элемента управления, чтобы он выполнялся в потоке пользовательского интерфейса, либо вам нужно настроить какой-то механизм связи между вызываемым потоком и потоком пользовательского интерфейса. Очевидно, что первое можно сделать просто с помощью TextBox.BeginInvoke:

txtSearch.BeginInvoke(sd, new object[] { keyword, orderBy, SearchCompleted });
0 голосов
/ 17 января 2010

Поскольку доступ к элементам управления Forms не является поточно-ориентированным, отладчик предупреждает вас, что вы нарушаете правила, обращаясь к нему из другого потока.Вместо этого вы можете Invoke элемент управления напрямую, чтобы получить желаемые результаты.Здесь есть отличный, всеобъемлющий учебник Microsoft о том, как это сделать здесь .

...