4

C#.NET では、次の単純なバックグラウンド ワーカー スレッドを作成しました。

public class MyBackgrounder
{
    public delegate void dlgAlert();
    public dlgAlert Alert;
    public event EventHandler eventAlert;
    Thread trd;

    public void Start()
    {
        if (trd == null || trd.ThreadState == ThreadState.Aborted)
        {
            trd = new Thread(new ThreadStart(Do));
        }
        trd.IsBackground = true;
        trd.Priority = ThreadPriority.BelowNormal;
        trd.Start();
    }

    void Do()
    {

        Thread.Sleep(3000);
        Done();
    }

    void Done()
    {
        if (Alert != null)
            Alert();
        if (eventAlert != null)
            eventAlert(this, new EventArgs());
        Kill();
    }

    public void Kill()
    {
        if (trd != null)
            trd.Abort();
        trd = null;
    }
}


static class Program
{

    [STAThread]
    static void Main()
    {
        MyBackgrounder bg = new MyBackgrounder();
        bg.eventAlert += new EventHandler(bg_eventAlert);
        bg.Alert = jobDone;
        bg.Start();
    }

    static void bg_eventAlert(object sender, EventArgs e)
    {
        // here, current thread's id has been changed
    }

    static void jobDone()
    { 
        // here, current thread's id has been changed
    }

}

3 秒間待機して (ジョブを実行)、指定されたイベントを発生させるか、デリゲートを呼び出します。ここまで問題はなく、すべて正常に動作します。しかし、「Thread.CurrentThread.ManagedThreadId」を見ると、それがバックグラウンド スレッドであることがわかります。多分それは正常ですが、どうすればこの動作を防ぐことができますか? 「 System.Windows.Forms.Timer」コンポーネントをテストし、その「 Tick」イベントを処理すると、「Thread.CurrentThread.ManagedThreadId」がメイン スレッド ID から他のものに変更されていないことがわかります。

私に何ができる?

4

5 に答える 5

5

Windows フォームを使用している場合は、次のようなことができます。

  1. フォームにプロパティを追加します

    private readonly System.Threading.SynchronizationContext context;
    public System.Threading.SynchronizationContext Context
    {
        get{ return this.context;}
    }
    
  2. あなたのフォームコンストラクターでプロパティを設定します

    this.context= WindowsFormsSynchronizationContext.Current;
    
  3. このプロパティを使用して、コンストラクター パラメーターとしてバックグラウンド ワーカーに渡します。このようにして、ワーカーは GUI コンテキストについて知ることができます。バックグラウンド ワーカー内に同様のプロパティを作成します。

    private readonly System.Threading.SynchronizationContext context;
    public System.Threading.SynchronizationContext Context
    {
        get{ return this.context;}
    }
    
    public MyWorker(SynchronizationContext context)
    {
        this.context = context;
    }
    
  4. Done()メソッドを変更します。

    void Done()
    {
        this.Context.Post(new SendOrPostCallback(DoneSynchronized), null);
    }
    
    void DoneSynchronized(object state)
    {
        //place here code You now have in Done method.
    }
    
  5. DoneSynchronized では、常に GUI スレッドにいる必要があります。

于 2012-06-30T13:10:19.403 に答える
1

タイマーはメイン スレッドで実行されます (メイン スレッドが非常にビジーな場合は、遅れて終了します)。何らかの理由でメインスレッドから jobDone() を呼び出す必要がある場合は、たとえば、内部でスレッド化され、メインスレッドでスレッドセーフと呼ばれる特定のイベントを持つ BackGroundWorker オブジェクトを使用できます (UI の更新などを許可します)。

于 2012-06-30T13:11:21.270 に答える
0

このデザインはどうですか:

  1. フォームに EventWaitHandle オブジェクトを作成します。

    private EventWaitHandle layer2changed =
        new EventWaitHandle(false, EventResetMode.ManualReset);
    
  2. タイマー イベント ハンドラーで、次の操作を行います。

    if (this.layer2changed.WaitOne(0, false))
    {
        // perform UI updates according to some public properties of layer1/layer2
        this.layer2changed.Reset();
    }
    
  3. コンストラクターで、layer2changed への参照を layer1/layer2 などに渡します。

  4. Done() メソッドが UI シグナルの更新を必要とする何かを実行するたびに、イベントが発生します。

     this.referencedLayer2Changed.Set();
    

ただし、layer2 と UI の変更には時間差があることに注意してください (Form.Timer 間隔が長いほど、差が長くなります)。

すべての人への注意: これは、彼の質問へのコメントで losssleep によって指定された追加要件から生まれた唯一の解決策です。反対票を投じる前にそれらを読んでください。:-)

于 2012-06-30T13:55:36.740 に答える
0

基になるレイヤー (レイヤー 2) の「System.Windows.Forms」への参照を必要としない別のソリューションがあると思います:
レイヤー 1 (UI フォーム) は、レイヤー 2 が使用できるタイムアウトの実装を提供します。

インターフェイスがあります:

namespace Layer2
{
    public delegate void TimeoutAlert();

    public interface IApplicationContext
    {
        void SetTimeout(TimeoutAlert timeoutAlerthandler, int milliseconds);
    }
}

Layer2 の労働者階級:

namespace Layer2
{
    public class Worker
    {
        IApplicationContext _context;
        public void DoJob(IApplicationContext context)
        {
            _context = context;
            _context.SetTimeout(JobTimedOut,3000);
            // nothing ... (wait for other devices or user events)
        }

        void JobTimedOut()
        {
            // do something suitable for timeout error with _context or anything else
        }

    }
}  

UI には次のようなものがあります。

using Layer2;

namespace Layer1
{
    public partial class frmTest : Form, IApplicationContext
    {

        int timeout;
        TimeoutAlert _timeoutAlerthandler;

        public frmTest()
        {
            InitializeComponent();
        }

        private void frmTest_Load(object sender, EventArgs e)
        {
            Worker w = new Worker();
            w.DoJob(this);
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            // it's the handler of 'Timer' component named 'timer1'
            timer1.Enabled = false;

            if (_timeoutAlerthandler != null)
                _timeoutAlerthandler();
        }

        #region IApplicationContext Members

        void IApplicationContext.SetTimeout(TimeoutAlert timeoutAlerthandler, int milliseconds)
        {
            _timeoutAlerthandler = timeoutAlerthandler;
            timeout = milliseconds;

            timer1.Interval = milliseconds;
            timer1.Enabled = true;
        }

        #endregion

    }
}  

私は自分のシナリオに再び気付きました:
いくつかの作業を行うためにコア プロジェクト (layer2) を使用する Windows アプリケーション プロジェクト (layer1) があります。コアは、別のスレッドを作成せずに、一部のメソッド中にノンブロッキング タイムアウト チェックを必要とします。これは、タイムアウト後に再び UI と対話する必要があるためです。少し複雑なので、これ以上説明できない場合は申し訳ありません (私にとっては!)。

于 2012-07-01T07:36:16.710 に答える
0

バックグラウンド スレッドであなたを悩ませているのは、正確には何ですか? あなたが説明するものは、これまでのところ私には問題ないようです。

比較のためにチェックした は UI スレッドSystem.Windows.Forms.Timerで実行されている可能性があり、実際には非バックグラウンド スレッドになります。

Thread.IsBackgroundプロパティの MSDN ドキュメントから(私によるハイライト):

スレッドは、バックグラウンド スレッドまたはフォアグラウンド スレッドのいずれかです。バックグラウンド スレッドは、バックグラウンド スレッドがプロセスの終了を妨げないことを除いて、フォアグラウンド スレッドと同じです。プロセスに属するすべてのフォアグラウンド スレッドが終了すると、共通言語ランタイムはプロセスを終了します。残りのバックグラウンド スレッドはすべて停止され、完了しません。

于 2012-06-30T11:13:56.557 に答える