任务/多线程时的Singleton可绑定控件

时间:2011-07-16 11:32:02

标签: c# multithreading data-binding singleton task

在发布问题之前,我做了10天的研究,所以真的希望有人可以解决这个问题。

问题是任何可绑定控件一旦更改了singleton类的绑定列表就不会更新。这是多线程应用程序的常见问题。大多数(如果不是全部)解决方案提供了从父线程初始化bindlinglist或集合的建议,然后进行了一些调用。不是我想要的。如果使用静态类而不是单例,则同样的问题仍然存在。

基本上,应用程序会触发一些任务,这些任务又会在不同的业务类上创建对象。这些对象将消息发布到绑定列表中,该列表应更新UI列表框,但不会。是的,消息对象在列表中,并且在 之后绑定 TASK完成工作(显示的项目)。锁定/解锁对象访问也不是问题。

感谢任何建议/解决方案

业务对象的精简版本:

namespace MyNameSpace
{
    public class Message
    {
        private string messageSummary;
        public Message() { }
        public string MessageSummary
        {
            set { messageSummary = value; }
            get { return messageSummary; }
        }
    }
}

执行某些操作的另一个类的精简版:

namespace MyNameSpace
{
    public class WorkDoingClass
    {
        public WorkDoingClass() { }
        public void DoSomeWork()
        {
            //some routines
            Message messageObj = new Message();
            messageObj.MessageSummary = "DoSOmrWork Finished";
        }

        public void DoSomeOtherWork()
        {
            //some routines
            Message messageObj = new Message();
            messageObj.MessageSummary = "DoSomeOtherWork Finished";
            AllMessages.Instance.AllMessagesBindingList.Add(messageObj);
        }
    }
}

的Singleton:

namespace MyNameSpace
{
    public sealed class AllMessages
    {
        private static readonly AllMessages _instance = new AllMessages();
        private BindingList<Message> _allMessagesBL;

        public WorkDoingClass() { _allMessagesBL = new BindingList<Message>(); }

        public static AllMessages Instance
        {
            get { return _instance; }
        }

        public BindingList<Message> AllMessagesBindingList
        {
            get { return _allMessagesBL};
        }
    }
}

这也是调用开始时的修剪版本:

namespace MyNameSpace
{
    public partial class Form1 : Form
    {
        private Task _TaskSqlData;
        private CancellationTokenSource cTokenSourceSql;

        public Form1()
        {
            InitializeComponent();
            listBox1.DataSource = AllMessages.Instance.AllMessagesBindingList;
            listBox1.DisplayMember = "MessageSummary";
        }

    private void button1_Click(object sender, EventArgs e)
    {
            cTokenSourceSql = new CancellationTokenSource();
            var tokenSqlData = cTokenSourceSql.Token;
            if (this._TaskSqlData != null)
            {
                if (this._TaskSqlData.Status == TaskStatus.Running)
                    this.cTokenSourceSql.Cancel();
                this._TaskSqlData.Dispose();
                this._TaskSqlData = null;
            }
            _TaskSqlData = Task.Factory.StartNew(()
                            => StartDoingWork(this, tokenSqlData, null), tokenSqlData);
    }

    public void StartDoingWork(object sender, CancellationToken ct, EventArgs e)
    {
            if (ct.IsCancellationRequested)
                ct.ThrowIfCancellationRequested();
            WorkDoingClass work = new WorkDoingClass();
            work.DoSomeOtherWork();
    }

1 个答案:

答案 0 :(得分:1)

您的问题是制作列表框的线程(主UI线程)与修改集合的线程(工作线程)不同。

尝试以下代码。它可以解决您的问题。我使用SynchronizationContext来同步两个线程,这两个线程与Control.Invoke()具有相同的功能。

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        private Task _TaskSqlData;
        private CancellationTokenSource cTokenSourceSql;
        WorkDoingClass _work;

        public Form1()
        {
            InitializeComponent();
            listBox1.DataSource = AllMessages.Instance.AllMessagesBindingList;
            listBox1.DisplayMember = "MessageSummary";
            _work = new WorkDoingClass(SynchronizationContext.Current);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            cTokenSourceSql = new CancellationTokenSource();
            var tokenSqlData = cTokenSourceSql.Token;
            if (this._TaskSqlData != null)
            {
                if (this._TaskSqlData.Status == TaskStatus.Running)
                    this.cTokenSourceSql.Cancel();
                this._TaskSqlData.Dispose();
                this._TaskSqlData = null;
            }
            _TaskSqlData = Task.Factory.StartNew(()
                            => StartDoingWork(this, tokenSqlData, null), tokenSqlData);
        }

        public void StartDoingWork(object sender, CancellationToken ct, EventArgs e)
        {
            if (ct.IsCancellationRequested)
                ct.ThrowIfCancellationRequested();

            _work.DoSomeOtherWork();
        }
    }

    public class Message
    {
        private string messageSummary;
        public Message() { }
        public string MessageSummary
        {
            set { messageSummary = value; }
            get { return messageSummary; }
        }
    }

    public class WorkDoingClass
    {
        private SynchronizationContext _syncContext;

        public WorkDoingClass() { }

        public WorkDoingClass(SynchronizationContext _syncContext)
        {
            // TODO: Complete member initialization
            this._syncContext = _syncContext;
        }
        public void DoSomeWork()
        {
            //some routines
            Message messageObj = new Message();
            messageObj.MessageSummary = "DoSOmrWork Finished";
        }

        public void DoSomeOtherWork()
        {
            _syncContext.Send(DoWork, null);
        }

        private static void DoWork(object arg)
        {
            //some routines
            Message messageObj = new Message();
            messageObj.MessageSummary = "DoSomeOtherWork Finished";
            AllMessages.Instance.AllMessagesBindingList.Add(messageObj);
        }
    }

    public sealed class AllMessages
    {
        private static readonly AllMessages _instance = new AllMessages();
        private BindingList<Message> _allMessagesBL;

        public AllMessages() { _allMessagesBL = new BindingList<Message>(); }

        public static AllMessages Instance
        {
            get { return _instance; }
        }

        public BindingList<Message> AllMessagesBindingList
        {
            get { return _allMessagesBL; }
        }
    }
}