2012-10-15 69 views
2

即时通讯构建一个桌面应用程序,每隔x秒间隔从数据库检索数据,以便将数据显示到winform,我不想阻止我的GUI,我不在乎如果方法检索数据需要一段时间,而这段时间少于检查间隔,我希望该方法与gui异步,什么是最好的方法,为什么?感谢新的线程!从gui执行异步方法的最佳方法

后台工作者? Thread.sleep? ManualResetEvent?

+3

使用'Task'类。 – SLaks

+0

请注意,'Task'类在.NET 4.0及更新版本中可用。 – Kamil

回答

0

正如卡米尔指出的,Tasks在.NET 4.0以前版本中不可用。

如果你陷入了黑暗时代,BackgroundWorker在这方面做得很好。事实上,这可能是它创造的!

未经测试的伪代码:

public partial class Form1 : Form { 

    private bool ok; 
    private BackgroundWorker worker; 

    public Form1() { 
    InitializeComponent(); 
    worker = new BackgroundWorker(); 
    worker.WorkerReportsProgress = true; 
    worker.WorkerSupportsCancellation = true; 
    worker.DoWork += new DoWorkEventHandler(worker_DoWork); 
    worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged); 
    worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted); 
    ok = true; 
    } 

    private void timerTick(object sender, EventArgs e) { 
    if (!worker.IsBusy) { 
     worker.RunWorkerAsync(); 
    } 
    } 

    private void worker_DoWork(object sender, DoWorkEventArgs e) { 
    var w = (BackgroundWorker)sender; 
    MyData inputData = (MyData)e.Argument; 
    for (int i = 0; (i < NUM_TASKS) && !worker.CancellationPending; i++) { 
     w.ReportProgress(i); 
     // do tasks 
    } 
    e.Result = SomethingYouWantReturned(); 
    } 

    private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e) { 
    if (!ok) { 
     worker.CancelAsync(); 
    } 
    } 

    private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { 
    if (e.Error != null) { 
     ok = false; 
     MessageBox.Show(this, e.Error.Message, "Error!"); 
    } else { 
     var item = (TypeYouWantedReturned)e.Result; 
     Console.WriteLine("Do something with `item`."); 
    } 
    } 

} 
+0

我有网络框架3.5,这个例子适合我!,但我不需要在DoWork事件中使用thread.sleep?例如: Thread.Sleep(1000);方法(参数); –

+0

跳过'Thread.Sleep(1000);'...而是将您的计时器设置为每1000毫秒打勾一次。这通常是在设计器中配置的,所以我不打扰如何手动编写代码。 – jp2code

2

所以,你开始说你想每X秒执行一次任务。这告诉我们我们需要一个计时器。虽然有很多选择。 System.Timer应该适用于我们的目的,但如果您想使用System.Windows.Forms.Timer,您可以。您需要在打开表单时启动计时器,将时间间隔配置为您想要的时间,并指定一个处理程序以在计时器触发时运行。

接下来,当计时器触发时,您需要执行数据库调用并更新UI。执行长时间运行的操作,然后使用结果更新UI是BackgroundWorker类专门为其量身定制的。设置DoWork方法来执行数据库调用,并根据结果让WorkerCompleted事件更新UI。 (已完成的事件将自动在UI线程中运行;您不需要手动编组到该线程。)

如果您有足够新的C#版本,您也可以使用任务来完成相同的任务。您可以使用Task.Factory.StartNew进行数据库调用,使其在后台线程中运行,然后您可以调用ContinueWithTask它返回并使用允许您指定同步上下文的重载。这里有一个简单的例子:

private void handler(object sender, EventArgs e) 
{ 
    Task.Factory.StartNew(() => getInfoFromDB()) 
     .ContinueWith(task => label1.Text = task.Result, 
     CancellationToken.None, TaskContinuationOptions.None, 
     TaskScheduler.FromCurrentSynchronizationContext()); 
} 

private string getInfoFromDB() 
{ 
    Thread.Sleep(2000);//simulate long IO operation 
    return "Hello world!"; 
} 

请注意,本Task基于例如工作,你就需要使用System.Windows.Forms.Timer计时器,以便Tick事件在UI线程中运行。

+0

@dotNETbeginner一般而言,您是正确的,这是我如何使用这个特定的任务,需要它使用窗体计时器。对'FromCurrentSynchronizationContext'的调用抓取当前上下文。如果当前上下文是后台线程,则后续将在后台线程中运行。我们需要“当前上下文”作为UI线程,并且窗体计时器将确保事件处理程序在UI线程中运行。还有其他方法可以在不使用窗体计时器的情况下访问同步上下文,我只是选择这样做。 – Servy