У меня есть консольное приложение, которое я пытаюсь запустить только один раз за раз. Для этого я использовал библиотеку boost interprocess shared_memory_object. Смотрите фрагмент кода ниже,
boost::scoped_ptr<shared_memory_object> sharedMem;
try
{
sharedMem.reset(
new shared_memory_object(create_only, "shared_memory", read_write));
} catch(...)
{
// executable is already running
cerr << "Another instance of this program is running!" << endl;
return 1;
}
// do something
shared_memory_object::remove("shared_memory"); // remove the shared memory before exiting the application
Дело в том, что этот метод не позволяет моему приложению запускаться более одного раза одновременно; однако давайте предположим, что пользователь останавливает выполнение программы, тогда память не будет освобождена, и в следующий раз, когда пользователь попытается снова запустить программу, она не запустится. У вас есть какие-нибудь предложения ?
Постскриптум Консольное приложение C ++, ОС: Ubuntu (но решение, которое будет работать и на других платформах, было бы идеальным).
Спасибо
Что вам нужно сделать, так это перехватить неожиданные завершения программы и соответственно освободить объект общей памяти. Вы можете поймать SIGINT
следующим образом, используя signal.h
POSIX заголовок:
#include <signal.h>
void handleSIGINT(int param=0) {
// Reset the shared memory object here
}
int main() {
// Connect the signal handler to SIGINT
signal(SIGINT, handleSIGINT);
// Etc...
}
И вы можете поймать завершение программы таким же образом, используя atexit()
функция. Документация Вот.
УВЕДОМЛЕНИЕ Ответ телепортирован из Как ограничить количество запущенных экземпляров в C ++. Он здесь, поскольку он детально описывает портативное решение с использованием Boost Interprocess и Boost Asio.
Обратите внимание, что решение является более общим, в том смысле, что вы можете использовать его для ограничения экземпляров до определенного максимума, а не просто 1
В Linux (и, возможно, в других ОС?) Вы можете использовать идиому файла блокировки (но она не поддерживается некоторыми файловыми системами и старыми ядрами).
Я бы предложил использовать межпроцессные объекты синхронизации.
Например, используя Boost Interprocess с именем семафор:
#include <boost/interprocess/sync/named_semaphore.hpp>
#include <boost/thread.hpp>
#include <cassert>
int main()
{
using namespace boost::interprocess;
named_semaphore sem(open_or_create, "ffed38bd-f0fc-4f79-8838-5301c328268c", 0ul);
if (sem.try_wait())
{
std::cout << "Oops, second instance\n";
}
else
{
sem.post();
// feign hard work for 30s
boost::this_thread::sleep_for(boost::chrono::seconds(30));
if (sem.try_wait())
{
sem.remove("ffed38bd-f0fc-4f79-8838-5301c328268c");
}
}
}
Если вы запустите одну копию на заднем плане, новые копии будут «отказываться» запускаться («К сожалению, второй экземпляр») в течение 30 секунд.
У меня есть ощущение, что здесь может быть проще изменить логику. Ммм. Дай мне попробовать.
проходит некоторое время
Хехе. Это было сложнее, чем я думал.
Дело в том, что вы хотите убедиться, что блокировка не сохраняется, когда ваше приложение прерывается или уничтожается. В интересах совместного использования методов переносной обработки сигналов:
#include <boost/interprocess/sync/named_semaphore.hpp>
#include <boost/thread.hpp>
#include <cassert>
#include <boost/asio.hpp>
#define MAX_PROCESS_INSTANCES 3
boost::interprocess::named_semaphore sem(
boost::interprocess::open_or_create,
"4de7ddfe-2bd5-428f-b74d-080970f980be",
MAX_PROCESS_INSTANCES);
// to handle signals:
boost::asio::io_service service;
boost::asio::signal_set sig(service);
int main()
{
if (sem.try_wait())
{
sig.add(SIGINT);
sig.add(SIGTERM);
sig.add(SIGABRT);
sig.async_wait([](boost::system::error_code,int sig){
std::cerr << "Exiting with signal " << sig << "...\n";
sem.post();
});
boost::thread sig_listener([&] { service.run(); });
boost::this_thread::sleep_for(boost::chrono::seconds(3));
service.post([&] { sig.cancel(); });
sig_listener.join();
}
else
{
std::cout << "More than " << MAX_PROCESS_INSTANCES << " instances not allowed\n";
}
}
Там можно многое объяснить. Дайте мне знать, если вы заинтересованы.
НОТА Должно быть совершенно очевидно, что если
kill -9
используется в вашем приложении (принудительное завершение), тогда все ставки отключены, и вам придется либо удалить объект Name Semaphore, либо явно разблокировать его (post()
).
Вот тестовый прогон в моей системе:
sehe@desktop:/tmp$ (for a in {1..6}; do ./test& done; time wait)
More than 3 instances not allowed
More than 3 instances not allowed
More than 3 instances not allowed
Exiting with signal 0...
Exiting with signal 0...
Exiting with signal 0...
real 0m3.005s
user 0m0.013s
sys 0m0.012s