Потоки и события Windows Forms - самый эффективный способ передачи событий? - PullRequest
1 голос
/ 25 сентября 2008

Моя форма получает асинхронные обратные вызовы от другого объекта в случайных рабочих потоках. Я передаю данные в основной поток (где их можно использовать для обновления экранных элементов управления), используя делегатов, как показано ниже. Производительность ужасна - когда я достигаю 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 );
}

Ответы [ 4 ]

2 голосов
/ 26 сентября 2008

Я не большой поклонник таймеров, если вы хотите более ориентированный на события подход, попробуйте что-то вроде этого:

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, собирая записи журнала и добавляя их в круговой массив, связанный с сеткой. Спектакль оказался довольно хорошим для того, что он делает.

1 голос
/ 25 сентября 2008

Я делал то, что предлагал Илья. Для интерфейсов, которые не должны отвечать «в реальном времени», у меня есть секундомер, который идет два раза в секунду или около того. Для более быстрого обновления я использую очередь или другую структуру данных, в которой хранятся данные о событиях, а затем использую «lock (queue) {}», чтобы избежать конфликта. Если вы не хотите замедлять рабочие потоки, вы должны убедиться, что поток пользовательского интерфейса не блокирует рабочих слишком долго.

1 голос
/ 25 сентября 2008

Возможно, вам не нужно обновлять пользовательский интерфейс при каждом событии, а скорее "не так часто, как X раз в секунду". Вы можете использовать StopWatch или другую систему синхронизации для сбора событий в течение определенного периода времени, а затем обновлять пользовательский интерфейс, когда это необходимо.

Если вам нужно захватить все события, собирайте их в Очередь и запускайте событие так часто, и этот обработчик событий будет обрабатывать Очередь и обновлять пользовательский интерфейс один раз для всех событий в очереди.

0 голосов
/ 25 сентября 2008

Трудно сказать точную проблему, но некоторые возможности ...

Ваш MyStatus объект, который вы передаете OnStatus, получен из MarshalByRefObject (и каждого объекта в нем)? Если нет, то он будет смещен при каждом маршалинге вызова, что может привести к огромному падению производительности.

Кроме того, перед вызовом делегата с помощью элемента управления вы должны действительно позвонить this.InvokeRequired, но на самом деле это просто лучшая практика.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...