Я хочу повторно запустить тему - PullRequest
0 голосов
/ 29 сентября 2019

Я реализую нить в стиле Java в C ++.Для этого у меня есть функция Thread :: Start, но моя проблема в том, что когда я ее использую, она отлично работает только в первый раз, по какой-то причине, когда вызов экземпляра потока запускает ее по завершении, но я не могу вызвать start сноваи ни один из них не вызывает функцию запуска другого потока.

Это мой конструктор потока:

Thread::Thread()
{
    m_handle = (HANDLE)_beginthreadex(0, 0, &call, this, CREATE_SUSPENDED, 0);
}

Деструктор потока:

Thread::~Thread()
{
    if (m_handle) CloseHandle(m_handle);
}

Вызов функции, которая используется в качестве оболочки для функции Run.:

unsigned int __stdcall Thread::call(void* _this)
{
    ((Thread*)_this)->Run();
    return 0;
}

И это моя функция запуска:

void Thread::Start()
{
    if (m_handle) ResumeThread(m_handle);
}

И код, который имеет проблему, следующий:

Класс клерка:

struct Clerk : public Thread
{
    Clerk()
    {
        IsOnService = false;
    }
    void Run()
    {
        std::cout << this->GetId() << " receive new customer " << '\n';
        Sleep(2000);
        std::cout << this->GetId() << " has finished" << '\n';
        IsOnService = false;
    }

    bool IsOnService;
};

Класс обслуживания:

struct Service
{
    Clerk*      clerk;
    Customer*   customer;
    Service(Clerk* c, Customer* cu)
    {
        clerk = c;
        customer = cu;
        clerk->Start();
    }
    ~Service()
    {
        delete customer;
    }
};

И основная функция:

int main()
{
    int nClerks = 5;
    Clerk* clerks[] = { 
        new Clerk(), new Clerk(), new Clerk(), new Clerk(), new Clerk()
    };
    Queue<Customer*> awaitingCustomers;
    while (1) 
    {
        if (GetAsyncKeyState(0x43) & 0x7FFF) 
        {
            Customer* newCustomer = new Customer();
            awaitingCustomers.Push(newCustomer);
            while (!awaitingCustomers.IsEmpty())
            {
                for (int i = 0; i < nClerks; ++i)
                {
                    if (!clerks[i]->IsOnService)
                    {
                        Service* newService = new Service(clerks[i], awaitingCustomers.Pop());
                        delete newService;
                        break;
                    }
                }
            }
        }
    }
    for (int i = 0; i < nClerks; ++i) delete clerks[i];
    return 0;
}

Когда я запускаю приведенный выше код, он вызывает функцию запуска только один раз, а не каждый разв этом я нажимаю клавишу «с».Мне нужно решение C ++ 98, спасибо.

1 Ответ

1 голос
/ 29 сентября 2019

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

Хорошая новость заключается в том, что есть несколько вещей, которые вы можете сделать, которые в любом случае эффективно дадут вам желаемое поведение:

1) Вызовите WaitForSingleObject(m_handle, INFINITE); и CloseHandle(m_handle);, чтобы очистить старый / вышедший поток, затем запустите новый поток так же, как вы запустили исходный поток (то есть, вызвав _beginthreadex() и ResumeThread() и т. Д.).Эта логика может быть инкапсулирована внутри вашего Thread класса, так что в отношении любого вызывающего кода поток выглядит «перезапущенным» (хотя и с другим идентификатором потока, если это имеет значение).

или, альтернативно (если вы не хотите каждый раз запускать новый поток):

2) Никогда не выходите из вашего потока в первую очередь.То есть ваша функция thready-entry должна выполнить что-то вроде этого (псевдокод):

unsigned int __stdcall Thread::call(void* _this)
{
   while(timeToQuit == false)
   {
      // Do the normal Thread thing
      ((Thread*)_this)->Run();

      EnterCriticalSection(...);
      // we'll block here until the main thread wakes us up 
      SleepConditionVariableCS(...);
      LeaveCriticalSection(...);
   }
   return 0;
}

Затем, когда ваш основной поток захочет «перезапустить» внутренний поток, он вызовет WakeConditionVariable в условной переменной объекта Thread, чтобы вывести внутренний поток из спящего режима после запуска, а затем внутренний поток снова вызовет Run().(И если вы хотите, чтобы внутренний поток завершил работу, вы должны сделать то же самое, за исключением того, что сначала установите для timeToQuit значение true - не то, что timeToQuit должно быть std::atomic<bool> или аналогичным, чтобы быть потокобезопасным)

...