Гараж Delphi




Алгоритмы и функции для эффективной разработки приложений в среде программирования Delphi

Ограничение количества потоков

13.04.2010 от semen

Для получения данных из сети удобно использовать несколько сетевых потоков одновременно. Одной из проблем является ограничение максимального количества потоков.

Самым простым и очевидным решением будет выделение переменной-счётчика, где и будет хранится число потоков. При создании нового потока можно увеличивать счётчик и, таким образом, знать текущее количество потоков - здесь всё просто и безопасно. Проблемным местом данного решения является уменьшение значения счётчика при уничтожении одного из потоков. Дело в том, что нужно обеспечивать эксклюзивный доступ к переменной счётчика и ни один другой поток не должен в этот момент её считывать или изменять. Поясню на примере: поток А и Б завершаются одновременно и каждый из потоков уменьшает значение счётчика на единицу. Допустим, перед завершением потоков, значение счётчика равнялось двум (всего было 2 потока). Каждый поток получает значение счётчика (двойку) одновременно и уменьшает на единицу (значение счётчика становится единицей), при этом потоки “не знают” о существовании друг друга. В таком варианте развития событий, значение счётчика не будет соответствовать реальном числу потоков, что обязательно приведёт к сбоям в работе программы и лишает смысла наличие самого счётчика. Для обеспечения эксклюзивного доступа можно воспользоваться критическими секциями или семафорами. Использовать критические секции и семафоры нужно очень осторожно, я бы не советовал их на начальном этапе изучения потоков в Дельфи. Мы воспользуемся другим способом, довольно простым в реализации.

Как правило, для получения данных из сети используются несколько экземпляров одного класса-потока, поэтому допустимо использование общего “контейнера” для всех используемых потоков одного типа. Я использовал класс TThreadList. При создании нового потока нужно добавлять его в ThreadList:

constructor TMyThread.Create;
begin
    inherited Create(True);
    ............
    ThreadList.Add(self);
end

При уничтожении потока, убираем его из списка:

destructor TMyThread.Destroy;
begin
    ................
    ThreadList.Remove(self);
    inherited Destroy;
end

Сразу же возникает вопрос - что произойдёт, если потоки будут уничтожаться и удаляться из списка потоков одновременно? Ответ прост - в реализации процедур Add и Remove используются критические секции для обеспечения эксклюзивности доступа к списку (кстати, полезно изучить реализацию этих процедур, если в будущем планируется использование критических секций), поэтому похожая ситуация со счётчиком гарантированно не возникнет. Узнать текущее количество потоков можно простой функцией:

function getThreadCount : integer;
begin
    Result := ThreadList.LockList.Count;
    ThreadList.UnlockList;
end;

LockList- блокирует ThreadList для других потоков, UnlockList - снимает эту блокировку. Таким образом, пока мы считываем количество потоков никаких изменений со списком не произойдёт.

ThreadList удобно использовать и в других целях, когда необходимо обратиться к созданным потокам (например - экстренное завершение).

См. также:
Настройка потока перед запуском
Ошибка в модуле idZLibCompressorBase
Обрабатываем HTML

Рубрики: Алгоритмы, Сеть, Общее

Оставьте комментарий

Заметьте: комментарии проверяются автором сайта. Нет смысла отправлять комментарий два раза.