2011-08-09 10 views
2

私は、UI、カスタムクラス、およびスレッドを持っています。私は別のスレッドで完全にカスタムクラスを実行したい。これを行うきれいな方法がありますか?別のスレッドでカスタムクラスのメソッド呼び出しを実行するには?

たとえば、下のメインフォームで、UIが_threadOneClass.Sleepを呼び出すとき、私は生成されたThreadOneに行き、メインスレッドではなくThreadOneでSleepメソッドを呼び出すためのUIが必要です。

基本的に、MyClassのすべてのメソッド呼び出しは、メインスレッドではなく、ThreadOneで実行する必要があります。それはMyClassが独自の「プロセス」上で動作するようなものですが、MainFormから呼び出されることはまだありません。

メインフォームには3つのボタンがあり、ロギングには1つのテキストボックスがあります。

私はThreadクラスを派生させることを考えていましたが、シールされています。だから、派生は間違いなくMicrosoftごとに間違った方法です。

貴重な専門家を助けるか?ここ

ここ出力(MainThread ID = 10、ThreadOne ID = 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スレッドをブロックするとユーザーに応答せず、オプションではないためです。

幸いにも、Microsoftの人々は、同じコードを書くのは非常に簡単ですが、非同期で行っています。私は通常、タスクを使用します。なぜなら、操作上のコントロールだけでなく、ContinueWith()は、呼び出しスレッドにデータを戻す必要がある場合に、結果を使って何をするかを制御できるからです。スレッドを使用したい場合は、ThreadPool.QueueUserWorkItemも簡単です。私はこれは私が同じ正確なコードを書くことができますが、ブロックせずに見つける

あなたは

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

または

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

、このようにそれをラップUIスレッドをブロックしたくない操作を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; 

実際の例を与えるために、ここではWPFのフロントエンドを持っていた私が書いたFTPクライアントからいくつかのコードがあります。スタートボタンをクリックすると、それ自身のタスクで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にはThread(ThreadStart(MethodTwo))のみが含まれていましたか?最初にインスタンス化されたスレッドへの参照はなくなりました。 –

0

これまでのところ、これは私が(iPhoneの開発から)見つけたものです。 Runループは、さまざまなメソッドを呼び出す背骨のように動作します。以下のように実装されています:
より洗練されたソリューションが歓迎されます。

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"); 
    } 
} 
関連する問題