Использование потоков для подсчета циклов в событиях C # - PullRequest
2 голосов
/ 22 января 2009

РЕДАКТИРОВАТЬ: Это не список. Виноват. Это список.

У меня есть элемент управления представлением списка, который сводит меня с ума. Это список со множественным выбором, поэтому, если пользователь выбирает 5000 строк, а затем отменяет их выбор, выбирая одну строку, SelectedIndexChanged срабатывает 5001 раз. Это приводит к зависанию моего приложения.

Я пытаюсь использовать потоки, чтобы подсчитать, сколько раз событие сработало, и затем позволить последней итерации выполнить всю фактическую работу.

Вот код, с которого я начал. Большая загвоздка: Мне нужно, чтобы "сделать причудливые вычисления" были в той же цепочке, что и вызывающие события из-за элементов, находящихся вне моего контроля.

РЕДАКТИРОВАТЬ: Я знаю , что этот код не работает. Join () блокирует текущий поток, что сводит на нет всю цель создания потока. Мой вопрос: как мне сделать что-то подобное?

Моя самая большая проблема не в создании темы. Дело в том, что мое "воображение" должно быть в одной теме.

</p> <pre><code> void IncrPaintQueue() { PaintQueue++; Thread.Sleep(100); } int PaintQueue = 0; private void SegmentList_SelectedIndexChanged(object sender, EventArgs e) { // We need to know how many threads this may possibly spawn. int MyQueue = PaintQueue; // Start a thread to increment the counter. Thread Th = new Thread(IncrPaintQueue); Th.IsBackground = true; Th.Start(); Th.Join(); // if I'm not the last thread, then just exit. // The last thread will do the right calculations. if (MyQueue != PaintQueue - 1) return; // Reset the PaintQueue counter. PaintQueue = 0; // ... do fancy calculations here... }

Ответы [ 5 ]

2 голосов
/ 22 января 2009

Я помню, как решал эту проблему до :

Возможно, лучшим способом для вас будет поставить минимальную задержку в вашем ItemSelectionChange Обработчик. Сказать -- 50мс. Используйте таймер, после выбора изменения, перезапустите таймер. Если выбор изменился более одного раза в течение периода задержки, то оригинал игнорируется, но после задержка истекла, логика казнены.

Как это:

public class SelectionEndListView : ListView
{
private System.Windows.Forms.Timer m_timer;
private const int SELECTION_DELAY = 50;

public SelectionEndListView()
{
   m_timer = new Timer();
   m_timer.Interval = SELECTION_DELAY;
   m_timer.Tick += new EventHandler(m_timer_Tick);
}

protected override void OnSelectedIndexChanged(EventArgs e)
{
   base.OnSelectedIndexChanged(e);

   // restart delay timer
   m_timer.Stop();
   m_timer.Start();
}

private void m_timer_Tick(object sender, EventArgs e)
{
   m_timer.Stop();

   // Perform selection end logic.
   Console.WriteLine("Selection Has Ended");
}
}
1 голос
/ 22 января 2009

У меня сложилось впечатление, что вы пытаетесь решить проблему с помощью грубой силы. Я бы предложил попробовать другое событие:

private void myListView_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e)
{
    if (e.IsSelected)
    {
        // do your logic here
     }
}

Я бы посоветовал избегать создания потоков, если это вообще возможно, так как они перегружены. Я не мог видеть из вашего примера, где есть необходимость в параллелизме.

1 голос
/ 22 января 2009

Возможное решение - отложить работу, чтобы вы знали, было ли запущено больше событий. Это предполагает, что порядок выбора не важен; все, что имеет значение, это текущее состояние.

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

Вы также можете выполнять работу в другом потоке, но иметь флаг, указывающий, что работа выполняется. Если, когда происходит событие выбора, работа все еще выполняется, вы устанавливаете флаг, который указывает, что работа должна быть повторена. Установка 'repeat_work' в истинное значение 5000 раз обходится не дорого.

0 голосов
/ 22 января 2009

Вы не можете достичь какого-либо параллелизма, запустив новый поток и сразу же присоединившись к нему. Единственный «эффект» приведенного выше кода заключается в том, что ваш метод запускается другим потоком.

Кроме того, если вы хотите использовать фоновый поток и сэкономить на довольно дорогих затратах на его создание, вам следует использовать пул потоков.

0 голосов
/ 22 января 2009

Во-первых, пока вы правильно синхронизируете доступ к PaintQueue, я чувствую, что в этой ситуации это было скорее случайным, чем дизайном. Если у вас есть другой код, обращающийся к PaintQueue в других потоках, у вас есть проблема.

Во-вторых, этот код не имеет смысла. Вы создаете новый поток, увеличиваете значение в этом потоке и ждете 1/10 секунды. Дело в том, что код, запускающий поток, ожидает завершения этого потока. Из-за этого вы просто ждете в потоке пользовательского интерфейса ничего.

Даже если вы поставите в очередь события SelectedIndexChange, вы не сможете предотвратить зависание своего приложения. Событие SelectedIndexChange будет запускаться каждый раз, когда вы выбираете элемент, и если пользователь выбирает 5000 элементов, вам нужно обработать все 5000 событий. Вы можете дать им окно (обрабатывать каждые n секунд или что угодно), но это довольно произвольно, и вы кладете пользователя на таймер, что плохо.

Что вы должны сделать, это не связать операцию с событием SelectedIndexChanged. Вместо этого попросите пользователя выбрать элементы, а затем попросить их выполнить какое-либо другое действие (например, нажать кнопку), которое будет работать с выбранными элементами.

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

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