在单独的线程中调用方法

时间:2012-02-20 09:59:33

标签: c# wpf multithreading

我需要在单独的线程中调用类方法。该方法有一个参数和一个返回值。

详情

我有一个带有两个TextBox的表单。用户将值放在第一个TextBox中,并将结果输入第二个TextBox:

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

Wiki-class必须使用Wikipedia 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 个答案:

答案 0 :(得分:10)

或者像这样使用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

答案 1 :(得分:2)

使用BackgroundWorker课程。这将允许您异步运行您的操作。然后,当BackgroundWorker通知它已完成操作时,您可以在Dispatcher线程上更新UI。

答案 2 :(得分:1)

另请参阅HttpWebRequest.BeginGetResponse以及有关如何异步执行Web请求的相关方法。这将消除任何自定义后台处理的需要。

答案 3 :(得分:0)

Youc可以使用WPF Dispatcher在主UI线程中执行操作,因为您已在WaitOne()中使用DoWork - 主DoWork操作将是同步的,因此您可以使用Dispatcher.BeginInvoke()来推送UI线程异步更改:

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

请告诉我,这是否适合您,因为Polity提到了一些可能的同步问题

答案 4 :(得分:0)

不要等待ThreadPool线程在UI线程中完成,而是订阅完整的事件:

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来确保在UI线程(sll already stated)中处理DoWorkComplete事件。如果您使用的是Windows窗体,则必须对Control.Invoke执行相同的操作。

答案 5 :(得分:0)

您可以使用异步调用来实现这一点,

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