2012-02-20 61 views
4

我需要在單獨的線程中調用類方法。該方法有一個參數和一個返回值。獨立線程調用方法

詳細

我有兩個文本框表單。用戶把值在第一個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?

回答

9

或使用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

+0

如果你可以使用.Net 4,那麼你應該肯定使用TPL(System.Threading.Tasks)。這也可以讓你跟蹤任何未完成的任務並更容易地取消它們。 例如,用戶在文本框中輸入「abcd」,並將任務引發到wiki。 在完成之前,用戶輸入「efgh」。 如果「efgh」查找完成* first *,那麼它將填充結果,這些將被「abcd」搜索覆蓋。 – Ragoczy 2012-02-20 14:51:00

2

看看使用BackgroundWorker類。這將允許您異步運行您的操作。當您收到BackgroundWorker通知完成操作時,您可以在分派器線程上更新您的UI。

+0

爲什麼我不能把我的BackgroundWorker的形式(http://imagebin.org/199796)? WPF應用程序。 – demas 2012-02-20 11:28:30

+0

看着你的圖像,'BackgroundWorker'組件被列爲WinForms組件。如果你在WPF中,你可能需要一個WinForms主機。或者,只是在後面的代碼中創建它。有關示例,請參閱[這裏](http://elegantcode.com/2009/07/03/wpf-multithreading-using-the-backgroundworker-and-reporting-the-progress-to-the-ui/)。 – 2012-02-20 11:49:28

0

Youc可以使用WPF調度,因爲你已經在DoWork使用WaitOne()在主UI線程來執行操作, - 主要DoWork的操作將是同步的,所以你可以使用Dispatcher.BeginInvoke()推在UI線程異步變化:

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

請ELT我知道這是否對你的作品,因爲政體提到的一些可能的同步問題

+0

這不會工作,由於同步問題 – Polity 2012-02-20 10:05:29

+0

@Polity:對不起,沒有得到一個點 – sll 2012-02-20 10:07:47

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 Forms,則必須使用Control.Invoke