Ограничение количества потоков
Для получения данных из сети удобно использовать несколько сетевых потоков одновременно. Одной из проблем является ограничение максимального количества потоков.
Самым простым и очевидным решением будет выделение переменной-счётчика, где и будет хранится число потоков. При создании нового потока можно увеличивать счётчик и, таким образом, знать текущее количество потоков - здесь всё просто и безопасно. Проблемным местом данного решения является уменьшение значения счётчика при уничтожении одного из потоков. Дело в том, что нужно обеспечивать эксклюзивный доступ к переменной счётчика и ни один другой поток не должен в этот момент её считывать или изменять. Поясню на примере: поток А и Б завершаются одновременно и каждый из потоков уменьшает значение счётчика на единицу. Допустим, перед завершением потоков, значение счётчика равнялось двум (всего было 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
