2011-08-09 88 views
2

我有一個用戶界面,一個自定義類和一個線程。我想在一個單獨的線程中完全運行自定義類。有沒有一種乾淨的方式來做到這一點?如何在單獨的線程中執行自定義類的方法調用?

例如。在下面的MainForm中,當UI調用_threadOneClass.Sleep時,我需要UI去到產生的ThreadOne並調用ThreadOne中的Sleep方法,而不是在主線程中。

基本上,MyClass中的所有方法調用都需要在ThreadOne中執行,而不是在主線程中執行。這就像MyClass在它自己的「進程」上運行,同時仍然可以從MainForm調用。

MainForm有3個按鈕和1個用於記錄的文本框。

我正在考慮派生Thread類,但它是密封的。因此,對每個微軟來說,推導絕對是一種錯誤的方式

幫助親愛的專家?

這裏是輸出(MainThread ID = 10,ID ThreadOne = 11)

 
MyClass instantiated 
Starting ThreadOne 
11-Run.start 
Sleeping ThreadOne 
10-Run.sleep for 3000 'Need this to run on ThreadID 11 
10-Run.woke up   'Need this to run on ThreadID 11 
Stopping ThreadOne 
11-Run.done 

下面是如何代碼看起來象。

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

    private Thread _threadOneThread; 
    private MyClass _threadOneClass; 

    private void btnThreadOneCreate_Click(object sender, EventArgs e) 
    { 
     _threadOneClass = new MyClass(this); 
     _threadOneThread = new Thread(new ThreadStart(_threadOneClass.Run)); 
     _threadOneThread.Start(); 
    } 

    private void btnThreadOneStop_Click(object sender, EventArgs e) 
    { 
     _threadOneClass.IsRunning = false; 
    } 

    private void btnThreadOneSleep_Click(object sender, EventArgs e) 
    { 
     _threadOneClass.Sleep(3000); 
    } 

    public void Log(string txt) 
    { 
     MainForm.SetText(txtLog, txt); 
    } 

    internal static void SetText(Control ctl, string val) 
    { 
     if (ctl.InvokeRequired) 
      ctl.Invoke((MethodInvoker)delegate() { ctl.Text += Environment.NewLine + val; }); 
     else 
      ctl.Text += Environment.NewLine + val; 
    } 
} 

class MyClass 
{ 
    public MyClass(MainForm frm) 
    { 
     _mainForm = frm; 
    } 
    private MainForm _mainForm; 
    public bool IsRunning = true; 
    public void Run() 
    { 
     _mainForm.Log(Thread.CurrentThread.ManagedThreadId.ToString() + "-Run.start"); 
     while (IsRunning) { } 
     _mainForm.Log(Thread.CurrentThread.ManagedThreadId.ToString() + "-Run.done"); 
    } 

    public void Sleep(int milliseconds) 
    { 
     _mainForm.Log(Thread.CurrentThread.ManagedThreadId.ToString() + "-Run.sleep for " + milliseconds.ToString()); 
     Thread.Sleep(milliseconds); 
     _mainForm.Log(Thread.CurrentThread.ManagedThreadId.ToString() + "-Run.woke up"); 
    } 
} 
+2

爲什麼你需要一個專用線程?你不能使用標準的Begin/End委託調用模式嗎? – cdhowie

+0

不是。這是一個複雜的場景,但基本上我正在爲我的C#winform調用一個單獨的進程打包。這個單獨的過程是專用和獨立運行的,所以我需要使這個包裝專用。沒有線程,我的C#winform很容易卡住.. –

回答

2

線程允許您在繼續執行其他操作時運行繁重的操作。在用戶界面(你的場景)的情況下,異步行爲幾乎總是必要的,因爲阻塞UI線程將導致對用戶不響應,而不是一個選項。

幸運的是,微軟的人們已經非常容易地編寫相同的代碼,但以異步的方式。我通常使用Tasks是因爲我喜歡通過操作獲得的控件,以及ContinueWith()讓您可以控制如何將結果傳播回調用線程。如果你更喜歡使用線程,ThreadPool.QueueUserWorkItem就像一樣簡單。

你不想阻止用戶界面線程包像這樣的任何操作,

Task.Factory.StartNew(() => Object.PerformOperation()); 

ThreadPool.QueueUserWorkItem(new WaitCallback((x) => Object.PeroformOperation())); 

我覺得這讓我寫完全相同的代碼,但沒有阻擋UI線程。如果你有幾條語句可以執行,你也可以使用一個塊。

Task.Factory.StartNew(() => 
{ 
    // do something 
    // do more stuff 
    // done 
}).ContinueWith((completedTask) => 
{ 
    // if you were computing a value with the task 
    // you can now do something with it 
    // this is like a callback method, but defined inline 

    // use ui's dispatcher if you need to interact with ui compontents 
    UI.Label.Dispatcher.Invoke(new Action(() => 
     UI.Item.Label.Text = completedTask.Result; 
} 

即將發佈的異步功能正在下一個.net版本中發佈,實際上更加簡化了這一點!但是由於它使用任務,您仍然希望能夠使用它們。

// this will begin the operation, then return control back to the ui so it does not hang. 
var result = await Object.PerformLongTask(); 

// once the long task is completed then it continues and you can use the result 
UI.Item.Label = result; 

舉一個真實的例子,這裏是一個FTP客戶端,我寫它有具有WPF前端一些代碼。當單擊開始按鈕時,ftp傳輸將在其自己的任務中啓動,然後在任務中啓動一個每半秒更新一次接口的while循環,因此既不干擾接口線程。再次,它是相同的代碼,只是包裹在lambada的。

private void btnStart_Click(object sender, RoutedEventArgs e) 
    { 
     Task.Factory.StartNew(() => 
      ftp.Mirror(@"C:\LocalFolder", "/RemoteFolder", 10)); 

     Task.Factory.StartNew(() => 
     { 
      while (true) 
      { 
       lbPercentSuccess.Dispatcher.Invoke(new Action(() => 
       { 
        lbPercentSuccess.Content = ftp.FtpProgress.SuccessPercentage; 
        lbPercentError.Content = ftp.FtpProgress.ErrorPercentage; 
        lbPercentTotal.Content = ftp.FtpProgress.TotalPercentage; 
        lbDuration.Content = ftp.FtpProgress.Duration; 
       })); 

       Thread.Sleep(500); 
      } 
     }); 
    } 
+0

看起來很有希望。當我有機會時,我會嘗試解決這個問題。謝謝。 –

+0

不客氣,我希望這會有所幫助。祝你好運! – Despertar

1

這是不可能的,據我所知。您只能運行和調用單個方法,或者在需要時將它們排隊在單獨的線程上。在一個單獨的線程上設置一個實際的對象會影響你的目的。這是因爲您只在單獨的線程而不是對象上調用方法時才利用多線程的好處。

然後將del重新分配給MethodTwo ...等等。如果符合方法簽名,這會變得更容易。

可能的解決辦法:

Thread threadTest = new Thread(new ThreadStart(MethodOne)); 
    threadTest = new Thread(new ThreadStart(MethodTwo)); 
    threadTest.Start(); 

或者

Action del = TestClass.MethodOne; 
    IAsyncResult result = del.BeginInvoke(null, null); 
    Func<int,int> del = TestClass.MethodOne; 
    IAsyncResult result = del.BeginInvoke(11,null, null); 
    int value = del.EndInvoke(result); 
+0

對於第一個解決方案,threadTest將包含線程(ThreadStart(MethodTwo))只是正確的?對第一個實例化線程的引用不見了。 –

0

到目前爲止,這是我發現(從iPhone的發展)。運行循環像調用各種方法的脊柱一樣工作。它的實施如下:
歡迎使用更優雅的解決方案。

class MyClass 
{ 
    public MyClass(MainForm frm) 
    { 
     _mainForm = frm; 
    } 
    private MainForm _mainForm; 
    public bool IsRunning = true; 
    public void Run() 
    { 
     _mainForm.Log(Thread.CurrentThread.ManagedThreadId.ToString() + "-Run.start"); 
     while (IsRunning) 
     { 
      if (_runSleepMilliSeconds != null) 
      { 
       _Sleep(_runSleepMilliSeconds ?? 3000); 
       _runSleepMilliSeconds = null; 
      } 
     } 
     _mainForm.Log(Thread.CurrentThread.ManagedThreadId.ToString() + "-Run.done"); 
    } 

    private int? _runSleepMilliSeconds = null; 
    public void Sleep(int milliseconds) 
    { 
     _runSleepMilliSeconds = milliseconds; 
    } 
    private void _Sleep(int milliseconds) 
    { 
     _mainForm.Log(Thread.CurrentThread.ManagedThreadId.ToString() + "-Run.sleep for " + milliseconds.ToString()); 
     Thread.Sleep(milliseconds); 
     _mainForm.Log(Thread.CurrentThread.ManagedThreadId.ToString() + "-Run.woke up"); 
    } 
} 
相關問題