2012-03-01 47 views
1

我在運行時運行一些腳本,但它凍結了我的UI,我在線程中調用CodeProvider,但它仍然凍結。如何等待線程退出該線程而不凍結我的APP?

在我的形式我打電話:

var mre = new ManualResetEvent(false); 
Thread tr = new Thread(() => 
    { 
     Script sp = new Script(); 
     code = textBox.Text; 
     sp.Comp(code); 
     mre.Set(); 
    }); 
tr.Start(); 
mre.WaitOne(); 

我使用mre.WaitOne()因爲我想等待線程完成繼續運行我的代碼。

嘗試使用編譯方法裏面也以同樣的方式:

public bool Comps(string code) 
{ 
    var mre = new ManualResetEvent(false); 
    Thread tr = new Thread(() => 
    { 
     //Code to generate a CompilerResult and generate the assembly 
     Run(); 
     mre.Set(); 
    }); 
    tr.Start(); 
    mre.WaitOne(); 
    return true; 
} 

但是,儘管它在等待它仍然凍結UI。

任何想法?

感謝

+0

如果您的應用程序基於WPF,[這](http://msdn.microsoft.com/en-us/magazine/cc163328.aspx)應該是相關的。 – sovanesyan 2012-03-01 21:56:05

+1

你的'mre.Set();'在線程的最後。這意味着'mre.WaitOne();'與'tr.Join();'相同,這意味着整個事物基本上是單線程的。 – 2012-03-01 22:12:56

回答

4

我使用mre.WaitOne(),因爲我想等待線程完成 繼續運行我的代碼。

如果您強制調用線程凍結,直到處理線程完成處理,您預期會發生什麼?這樣做,沒有多餘的線程,如果調用線程是UI線程,當然它會凍結。

如果你做後臺處理,你不能等待結果同步,而是你必須以某種方式通知用戶界面處理完成,即使用回調或將結果分發回其他用戶界面形成。

+0

+1 - 在GUI事件處理程序中等待,再次:(( – 2012-03-01 22:10:34

2

多線程的全部重點是允許線程獨立於任何其他線程執行。你想要做的就是使用回調來表示線程完成,然後讓你的UI響應完成。

BackgroundWorker類有一個事件已經內置於此目的。

有你想訂閱的三個事件:

bw.DoWork += 
    new DoWorkEventHandler(bw_DoWork); 
bw.ProgressChanged += 
    new ProgressChangedEventHandler(bw_ProgressChanged); 
bw.RunWorkerCompleted += 
    new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted); 

DoWork是你的工作會發生。 ProgressChanged允許您更新進度的UI。 RunWorkerCompleted將彈出事件與您的DoWork功能已完成。

該對象處理線程,可以設置爲通過運行bw.RunWorkerAsync()調用異步運行。

爲此請參閱詳細以下網頁:

http://msdn.microsoft.com/en-us/library/cc221403%28v=vs.95%29.aspx

作爲一個例子:

public partial class Form1 : Form 
{ 
    public Form1() 
    { 
     InitializeComponent(); 
    } 

    private void Form1_Load(object sender, EventArgs e) 
    { 
    } 

    private void button1_Click(object sender, EventArgs e) 
    { 
     MessageBox.Show(String.Format("UI thread: {0}", Thread.CurrentThread.ManagedThreadId)); 
     this.Invoke(new MethodInvoker(delegate() { MessageBox.Show(String.Format("Invoke thread: {0}", Thread.CurrentThread.ManagedThreadId)); })); 

     backgroundWorker1.RunWorkerAsync(); 
    } 

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
    { 
     MessageBox.Show(String.Format("Worker thread: {0}", Thread.CurrentThread.ManagedThreadId)); 
    } 
} 

該實施例可以通過增加一個按鈕,一個背景工人的形式來構建。通過事件設計器爲button1_Click和backgroundWorker1_DoWork函數連接事件。點擊按鈕1後應該彈出三個消息框。您會注意到UI線程的Id和Invoke線程是相同的,這意味着您從調用中執行的任何處理都將導致您的UI線程等待。第三個彈出窗口來自工作線程,該工作線程具有不同的ID。

+0

)謝謝,我剛剛發現如何使用它,它現在工作良好。 – Kyore 2012-03-02 01:51:10

+0

我試圖使用背景工作,我試圖解決它我的問題,但是當我看到它返回一個交叉線程異常,並且如果我使用: this.Invoke(new MethodInvoker(delegate(){sp.Comp(code);})); 它凍結了UI – Kyore 2012-03-03 03:31:48

+0

似乎你的處理過程仍然在主UI線程上發生,我已經在我的答案的最後添加了一個示例來向你展示行爲。 – Walk 2012-03-05 15:45:10

1

完成後使用BeginInvoke。例如:

delegate void MyAction(); 

void Form1_Load(object sender, EventArgs e) 
{ 
    Thread tr = new Thread(() => 
    { 
     Script sp = new Script(); 
     code = textBox.Text; 
     sp.Comp(code); 

     BeginInvoke(new MyAction(ThreadOperationEnded)); 
    }); 
    tr.Start(); 
} 

void ThreadOperationEnded() 
{ 
    MessageBox.Show("Finished!"); 
}