#c #multithreading
Вопрос:
Как и в названии. Функциональность, в которой я нуждаюсь, включает в себя уничтожение запущенного потока при определенных условиях. К сожалению, если указанный поток заблокировал определенный глобальный мьютекс, этот мьютекс останется заблокированным навсегда, поскольку я использую конструкцию, в которой один поток пытается получить мьютекс, но он освобождается только после выполнения некоторой работы из другого потока, что позволяет другому вызову в первом потоке повторно получить его снова. Поскольку первый поток убит, мьютекс никогда не разблокируется, а второй поток застрял и ничего не может с этим поделать.
Я бы разблокировал мьютекс после завершения потока (то есть из нового экземпляра того же потока), но, по-видимому, это недопустимо и приводит к неопределенному поведению, потому что мьютекс должен быть разблокирован из того же потока, который его заблокировал. Что я могу с этим поделать? Спасибо.
Комментарии:
1. Функциональность, в которой я нуждаюсь, включает в себя убийство запущенного потока при некоторых условиях , действительно, действительно плохая идея. У вас нет способа узнать, удерживает ли поток также блокировки, которые вы не контролируете.
2. Как насчет захвата сигнала об уничтожении в потоке и освобождения мьютекса в обработчике?
3. Вы не можете просто так отпереть такой замок. Эта блокировка защищала что-то от параллельного доступа, и вы думаете, что можете просто прервать что-то подобное в середине этого процесса, разблокировать блокировку, и все будет работать? Итак, когда убитый поток находился в середине
malloc()
вызова, когда куча заблокирована, как вы планируете безопасно убить этот поток? Ты не можешь .4. …или перепроектировать так, чтобы вам не нужно было завершать/убивать какие-либо потоки до тех пор, пока ОС не сделает это при завершении процесса.
5. Ни один поток никогда не должен заставлять какой-либо другой поток что-либо делать (например, умирать). Потоки должны взаимодействовать друг с другом. Если потоку A нужно, чтобы поток B умер, то поток A должен попросить поток B умереть, а поток B должен быть спроектирован так, чтобы быстро реагировать на запрос и аккуратно завершать себя.
Ответ №1:
Функциональность, в которой я нуждаюсь, включает в себя уничтожение запущенного потока при определенных условиях.
Потоки-это деталь реализации. Невозможно убить поток для функциональности, если только нет большого количества кода, который вы не можете контролировать. Если вы находитесь в этом случае, решение состоит в том, чтобы изолировать поток, который вы не можете контролировать, в его собственный процесс. Вы можете безопасно убить процесс.
В противном случае вы не сможете безопасно прервать поток. Но тебе и не нужно этого делать. Что вам нужно сделать, так это остановить поток от выполнения работы, которую вы не хотите, чтобы он выполнял, и заставить его выполнять ту работу, которую вы хотите.
Вы не описали работу, выполняемую потоком, или стандарт потоковой передачи, который вы используете, поэтому нелегко дать вам совет, как лучше всего это сделать. Но в целом существует два подхода:
- Имейте какое-то общее состояние, которое отслеживает, какую работу необходимо выполнить. Попросите код, который выполняет работу, которую, возможно, потребуется остановить, периодически проверять, следует ли останавливать работу.
- Используйте какой-нибудь метод сигнализации, чтобы сигнализировать потоку, выполняющему работу, которую, возможно, потребуется прервать, что выполняемая им работа, возможно, больше не потребуется. Это позволяет избежать необходимости периодических проверок, но, как правило, не устраняет необходимость в общем состоянии.
В крайнем случае, просто реализуйте свой собственный мьютекс, который поддерживает разблокировку после смерти потока. Таким образом, поток будет содержать только экземпляр этого мьютекса, который поддерживает эту функциональность. В вашей реализации будет внутреннее логическое значение, защищенное внутренним мьютексом, которое указывает, что мьютекс удерживается. Таким образом, когда поток умирает, вы можете просто получить внутренний мьютекс, очистить внутреннее логическое значение и освободить внутренний мьютекс.
Вот как выглядит ваша логика:
Чтобы заблокировать мьютекс:
- Получите внутренний мьютекс.
- Пока внутреннее логическое значение равно true, дождитесь внутренней переменной условия.
- Установите для внутреннего логического значения значение true.
- Освободите внутренний мьютекс.
Чтобы разблокировать мьютекс:
- Получите внутренний мьютекс.
- Установите для внутреннего логического значения значение false.
- Транслируйте переменную внутреннего состояния.
- Освободите внутренний мьютекс.
Теперь, после смерти потока, вы можете просто вызвать описанную выше операцию «разблокировать мьютекс». (Конечно, сначала убедитесь, что любое состояние, которое защищает мьютекс, является согласованным.)
Комментарии:
1. Спасибо за ответ, Дэвид. Как я уже упоминал в комментарии, поток фактически убивает себя. Так что я неправильно понял этот момент. В противном случае, это хороший момент, что не следует делать рутинное уничтожение потоков частью основной функциональности вашей программы. Тем не менее, вы, должно быть, отвечаете на другой вопрос. Вопрос был о том, как разблокировать мьютекс после завершения потока.
2. @swaggg Мой первый абзац касается этого вопроса-это невозможно сделать безопасно. Но есть очень большая вероятность, что вы сможете перепроектировать так, чтобы поток не удерживал мьютекс. Например, можете ли вы просто использовать логическое значение, защищенное мьютексом, вместо мьютекса? Затем вы можете просто получить мьютекс, изменить логическое значение и освободить мьютекс. (Нетрудно закодировать свой собственный мьютекс, который можно безопасно разблокировать после смерти потока.)
3. Да, это вызвало бы неопределенное поведение. Вопрос заключался скорее в том, что конкретно делать с таким мьютексом, когда его выпускать и как. Не о том, безопасно ли убивать поток или нет.
4. @swaggg Это истинное обобщение. Во-первых, подумайте, что произойдет, если поток будет уничтожен, пока он содержит какой-либо другой внутренний мьютекс, который не поддерживает уничтожение потока во время его удержания.
5. @swaggg Нет, неправда. Независимо от того, что вы вставляете в поток, реализация может реализовать все, что вы вставляете, с такими вещами, как внутренние мьютексы, состояние которых может быть повреждено в результате уничтожения потока. Если только под «убийством» вы не подразумеваете полное отключение, что было бы совершенно другим вопросом. (Windows славится этим.)
Ответ №2:
Как предположил @meaning-matters, это должно быть возможно, по крайней мере, в Linux, путем перехвата сигнала 32 в реальном времени и использования pthread_cancel, который реализован с использованием сигналов.