Windows窗体线程和事件-传递事件的最有效方法?

时间:2020-03-06 14:41:27  来源:igfitidea点击:

我的表单从随机工作线程上的另一个对象接收异步回调。我一直在使用委托将数据传递到主线程(可用于更新屏幕上的控件),如下所示。性能令人恐惧-每秒达到500次更新后,程序将完全锁定。我的GUI处理本身不是问题,因为我可以在表单中模拟这种级别的更新并且没有问题。我应该使用一种更有效的机制来将数据从一个线程传递到另一个线程吗?

delegate void DStatus( MyStatus obj );
DStatus _status; // set to MainThreadOnStatus during construction

// this function only called on form's owner thread
void MainThreadOnStatus( MyStatus obj )
{
   // screen updates here as needed
}

// this function called by arbitrary worker threads in external facility
void OnStatus( MyStatus obj )
{
   this.BeginInvoke( _status, obj );
}

解决方案

我们可能不需要在每个事件上都更新UI,而是"不如每秒X次"。我们可以利用StopWatch或者其他计时系统在一段时间内收集事件,然后在适当时更新UI。

如果我们需要捕获所有事件,请经常将它们收集在Queue中并触发事件,该事件处理程序将处理Queue并为所有排队的事件更新一次UI。

很难说出确切的问题,但是有一些可能性...

我们传递给OnStatus的MyStatus对象是否源自MarshalByRefObject(及其中的每个对象)?如果不是这样,它将在每次经过封送处理的呼叫中出现故障,这可能会导致巨大的性能损失。

另外,在使用控件调用委托之前,我们应该真正调用this.InvokeRequired,但这确实是最佳实践。

我一直在做Ilya一直在建议的事情。对于不需要"实时"响应的UI,我有一块秒表,大约每秒钟走一圈。为了进行更快的更新,我使用存储事件数据的队列或者其他数据结构,然后使用"锁定(队列){}"来避免争用。如果我们不想减慢工作线程,则必须确保UI线程不会太长时间阻塞工作线程。

我不是计时器的忠实拥护者,如果我们想要更多的事件驱动方法,请尝试以下方法:

public class Foo
{
    private AsyncOperation _asyncOperation = null;
    private SendOrPostCallback _notifyNewItem = null;

    //Make sure you call this on your UI thread.
    //Alternatively you can call something like the AttachUI() below later on and catch-up with
    //your workers later.
    public Foo()
    {
        this._notifyNewItem = new SendOrPostCallback(this.NewDataInTempList);
        this._asyncOperation = AsyncOperationManager.CreateOperation(this);
    }

    public void AttachUI()
    {
        if (this._asyncOperation != null)
        {
            this._asyncOperation.OperationCompleted();
            this._asyncOperation = null;
        }

        this._asyncOperation = AsyncOperationManager.CreateOperation(this);
        //This is for catching up with the workers if they’ve been busy already
        if (this._asyncOperation != null)
        {
            this._asyncOperation.Post(this._notifyNewItem, null);
        }
    }

    private int _tempCapacity = 500;
    private object _tempListLock = new object();
    private List<MyStatus> _tempList = null;

    //This gets called on the worker threads..
    //Keeps adding to the same list until UI grabs it, then create a new one.
    public void Add(MyStatus status)
    {
        bool notify = false;
        lock (_tempListLock)
        {
            if (this._tempList == null)
            {
                this._tempList = new List<MyStatus>(this._tempCapacity);
                notify = true;
            }

            this._tempList.Add(status);
        }
        if (notify)
        {
            if (this._asyncOperation != null)
            {
                this._asyncOperation.Post(this._notifyNewItem, null);
            }
        }
    }

    //This gets called on your UI thread.
    private void NewDataInTempList(object o)
    {
        List<MyStatus> statusList = null;
        lock (this._tempListLock)
        {
            //Grab the list, and release the lock as soon as possible.
            statusList = this._tempList;
            this._tempList = null;
        }
        if (statusList != null)
        {
            //Deal with it here at your leasure
        }
    }
}

香港专业教育学院在自定义Log4Net记录器中使用了此功能,以收集日志条目并将其添加到绑定到网格的圆形数组中。最终,它的性能相当不错。