C#跨线程通信(C# Cross-Thread communication)

2019-06-25 23:00发布

在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”组件和处理其“ ”的事件,你可以看到“Thread.CurrentThread.ManagedThreadId”尚未从主线程ID改为别的。

我能做什么?

Answer 1:

如果您使用的是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线程。



Answer 2:

定时器运行在主线程(并将于后期都会响起来,如果主线程是真的很忙)。 如果由于某种原因,你想jobDone()从主线程调用,你可以,例如使用的是内螺纹和具有被称为与主线程线程的一些具体事件的BackgroundWorker对象(允许UI更新等)



Answer 3:

到底是什么这是你的后台线程错误? 你描述的,看起来很好,对我来说至今。

System.Windows.Forms.Timer你检查比较可能的UI线程 ,这的确将使它在非后台线程运行。

从的MSDN文档Thread.IsBackground属性(由我亮点):

线程可以是一个背景线程或前台线程。 后台线程是相同的前景线程,所不同的是后台线程不会阻止从终止进程。 一旦属于一个进程的所有前台线程已经终止,公共语言运行时结束处理。 任何剩余的后台线程停止并没有完成。



Answer 4:

这个怎么样设计:

  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到层1 /层等在它们的构造函数的引用。

  4. 每次你做()方法​​做一些事情,需要更新UI信号的事件:

      this.referencedLayer2Changed.Set(); 

但是请注意,这将有二层和UI的变化(时间越长Form.Timer间隔 - 越长越差)之间的时间差。

注意给大家:这是一个负全部的解决方案由losingsleep在评论他的问题指定的附加要求不断涌现。 请阅读他们downvoting之前。 :-)



Answer 5:

我觉得有这并不需要在底层(二层)“System.Windows.Forms的”参照另一种解决方案:
第一层(UI形式)提供了一个超时的实现,二层可以使用。

有一个接口:

namespace Layer2
{
    public delegate void TimeoutAlert();

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

在二层工作类:

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

    }
}  

我又发现我的情况:
我有一个使用一个核心项目(二层)做一些工作Windows应用程序项目(层)。 核心需求中的一些方法无阻塞超时检查,而无需创建一个不同的线程,导致超时后需要再次与UI交互。 对不起,如果我无法用语言形容了,因为它是一个有点复杂(我!)。



文章来源: C# Cross-Thread communication