任务/多线程处理时的单例可绑定控件

发布于 2024-11-24 12:38:39 字数 3180 浏览 0 评论 0原文

在发布这个问题之前,我做了 10 天的研究,所以真的希望有人能够为解决这个问题提供一些线索。

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

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

感谢任何建议/解决方案

业务对象的精简版本:

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);
        }
    }
}

单例:

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();
    }

before posting the question i did my research for 10 days so really hope someone can shed some light into solving this issue.

The issue is that any bindable control, does not update once the binding list from singleton class is changed. This is a common issue on multi-threaded apps. Most if not all solutions offer suggestions where the bindlinglist or collection is initialized from parent thread, and then some invocation to be made. Not what i'm looking for. The same issue persist if static class is used instead of singleton.

Basically, the application triggers some Tasks, which in turn create object(s) on different business classes. These objects post messages into the bindinglist, which should update the UI listbox, but does not. And yes, the message object is in the list, and binding after the TASK finished works (items displayed). Locking/unlocking object(s) access is also not an issue.

Appreciate any suggestions/solutions

A trimmed down version of business objects:

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

A trimmed down version of another class doing some ops:

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};
        }
    }
}

This is also a trimmed down version from where calls start:

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();
    }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

樱娆 2024-12-01 12:38:39

您的问题是创建列表框的线程(主 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; }
        }
    }
}

Your problem is that the thread(the main UI thread) making the listbox is different from the thread(the worker thread) modifying the collection.

Try the following code. It could solve your issue. I use SynchronizationContext to synchronize the two threads, which serves as the same function with 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; }
        }
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文