2012-02-20 65 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