Вызов метода в отдельном потоке - PullRequest
4 голосов
/ 20 февраля 2012

Мне нужно вызвать метод класса в отдельном потоке. У метода есть параметр и возвращаемое значение.

Детали

У меня есть форма с двумя текстовыми полями. Пользователь помещает значение в первый TextBox и получает результат во второй:

 private void tbWord_TextChanged(object sender, TextChangedEventArgs e)
 {
     tbResponce.Text = Wiki.DoWork(tbWord.Text);
 }

Вики-класс должен использовать API Википедии:

public class Wiki
{
    private class State
    {
        public EventWaitHandle eventWaitHandle = new ManualResetEvent(false);
        public String result;
        public String word;
    }

    private static void PerformUserWorkItem( Object stateObject )
    {
        State state = stateObject as State;

        if(state != null)
        {
            Uri address = new Uri("http://en.wikipedia.org/w/api.php?action=opensearch&search=" + state.word);
            HttpWebRequest request = WebRequest.Create(address) as HttpWebRequest;
            HttpWebResponse responce = (HttpWebResponse)request.GetResponse();

            StreamReader myStreamReader = new StreamReader(responce.GetResponseStream(), Encoding.GetEncoding(1251));
            state.result = myStreamReader.ReadToEnd();

            state.eventWaitHandle.Set(); 
        }
    }

    public static String DoWork(String _word)
    {
        State state = new State();
        state.word = _word;

        ThreadPool.QueueUserWorkItem(PerformUserWorkItem, state);
        state.eventWaitHandle.WaitOne();
        return state.result;                   
    }     
}

Задача

Когда пользователь нажимает клавиши в tbWord, основная форма останавливается и ждет, пока класс Wiki сделает всю работу.

Как я могу запустить DoWork асинхронно?

Ответы [ 6 ]

11 голосов
/ 20 февраля 2012

Или используйте TPL следующим образом:

private void tbWord_TextChanged(object sender, TextChangedEventArgs e) 
 { 
    Task.Factory.StartNew(() =>
    {
        return Wiki.DoWrok(tbWord.Text);
    }).ContinueWith(taskState =>
    {
        tbResponce.Text = taskState.Result;
    }, TaskScheduler.FromCurrentSynchronizationContext());
 } 

см .: http://msdn.microsoft.com/en-us/library/dd997394.aspx

2 голосов
/ 20 февраля 2012

Изучите использование класса BackgroundWorker.Это позволит вам выполнить вашу операцию асинхронно.Затем вы можете обновить свой пользовательский интерфейс обратно в потоке диспетчера, когда вы получите уведомление от BackgroundWorker о завершении операции.

1 голос
/ 20 февраля 2012

Также взгляните на HttpWebRequest.BeginGetResponse и связанные методы о том, как асинхронно выполнять веб-запросы.Это исключило бы необходимость какой-либо пользовательской фоновой обработки.

0 голосов
/ 20 февраля 2012

Вы можете использовать асинхронный вызов, чтобы достичь этого,

http://support.microsoft.com/kb/315582

0 голосов
/ 20 февраля 2012

Не ждите завершения потока ThreadPool в вашем потоке пользовательского интерфейса, но вместо этого подпишитесь на полное событие:

class Program
{
    static void Main(string[] args)
    {
        var program = new Program();
        program.DoWorkComplete += (s, e) => Console.WriteLine(e.Result);
        program.DoWorkAsync();

        // Wait 10 seconds, worker thread should finish beforehand, so
        // you see console output
        Thread.Sleep(10000);
    }

    public event EventHandler<CompleteEventArgs> DoWorkComplete;

    public void DoWorkAsync()
    {
        ThreadPool.QueueUserWorkItem(o =>
        {
            // Download data here
            string result = "Result from Wiki";

            if(DoWorkComplete != null)
                DoWorkComplete(this, new CompleteEventArgs(result));
        });
    }

    public class CompleteEventArgs : EventArgs
    {
        public CompleteEventArgs(string result)
        {
            this.Result = result;
        }

        public string Result { get; private set; }
    }
}

Редактировать: Это консольное приложение. Я могу WriteLine из любой темы. Если вы работаете с WPF, вам нужно использовать Dispatcher, чтобы убедиться, что вы обрабатываете событие DoWorkComplete обратно в UI-потоке ( sll уже указано ). Если вы работаете с Windows Forms, вы должны сделать то же самое с Control.Invoke.

0 голосов
/ 20 февраля 2012

Вы можете использовать диспетчер WPF для выполнения операции в главном потоке пользовательского интерфейса, поскольку вы уже используете WaitOne() в DoWork - основная операция DoWork будет синхронной, поэтому вы можете использовать Dispatcher.BeginInvoke() для асинхронной передачи изменений в потоке пользовательского интерфейса:

Dispatcher.CurrentDispatcher.BeginInvoke(
        DispatcherPriority.Input, 
        new ThreadStart(() => 
          { 
             tbResponce.Text = Wiki.DoWork(tbWord.Text); 
          }));

Пожалуйста, дайте мне знать, работает ли это для вас, так как Polity упомянул некоторые возможные проблемы с синхронизацией

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...