0

Background: So here is my problem: I have a method inside of a certain class that will take user inputs until the user enters a "stop signal", say "done". Like:

void getInput(){
    string input;
    cin >> input;
    while (input != "done") {
        cout <<"Please input a word: ";
        linkedlist.push(input);
        cin>> input;
    }
}

Now I have to add a timer to limit this user input for a certain time, like 30 seconds, and the linkedlist will only record what the user has inputted within those 30 seconds, so I just need a way to break the loop.

Sample output:

You have 30 seconds to input the words or input "done" to finish:
Please input a word: pikachu
Please input a word: kapichu
Please input a word: chupika
Please input a word: kachupi
30 seconds has reached. Your inputs are saved.

I've just learned thread, but I noticed every tutorial put threads only in main(), and I've noticed this post and tried the provided method and it works in its case: How to use a timer in C++ to force input within a given time?.

But here, if I would use thread to achieve my task, it seems I need to restructure my class entirely or run the methods from that class one by one in main()?

Question: So, can we write threads in a normal function and use them accordingly? (derivative question: If not, what would be the best solution to realize it?)

I'm very new to concurrent programming. Please elaborate. Thanks guys!

Hang
  • 355
  • 3
  • 19
  • 2
    Why use concurrency for a timer when `alarm` exists? http://man7.org/linux/man-pages/man2/alarm.2.html – Ed Heal Nov 18 '17 at 14:45
  • 1
    @EdHeal - are you sure the question is about a POSIX system? In general there appears to be [no portable way](https://stackoverflow.com/a/28946894/485343) to do this. – rustyx Nov 18 '17 at 14:48
  • 1
    @RustyX - Just a possibility - Also timeSetEvent for windows – Ed Heal Nov 18 '17 at 14:50
  • What do you want to do? Cancel the loop while it is waiting for the next input or just break out of the loop after a certain time? The latter only requires you to check e.g. chrono::system_clock::now(). The former is not doable in standard c++ but there might be system specific APIs for this, in which case it would be helpful if you could tell which system you are running on. – MikeMB Nov 18 '17 at 15:17
  • @EdHeal I didn't know alarm, I will take a look at that and see if it can be used in my case. Thanks for your reply! – Hang Nov 18 '17 at 16:48
  • @RustyX I'm also not sure what is a POSIX system. I didn't even realize different system could present different results from the same code. I'm using Xcode on macOS, if this is what you are asking? – Hang Nov 18 '17 at 16:49
  • @MikeMB Thanks Mike, I should have made it more clear. I think I am more aligned to the latter one. I want to break out the loop and only record what the user has inputted during the time limit to the linked list. I will take a look at chrono::system_clock::now(). – Hang Nov 18 '17 at 16:51
  • @Ron Thanks for pointing that out Ron, My question is indeed not that clear to the title. Will make modifications. – Hang Nov 18 '17 at 16:52

2 Answers2

1

main() is just a function, much like any other (except its name is reserved and the CRT relies on it).

Yes, you can create threads from "any place" - be it a non-main() function, a class's static or non-static method, const or non-const, inline or not, public/protected/private, file-local, lambda, etc.

YePhIcK
  • 5,816
  • 2
  • 27
  • 52
  • Thanks YePhlcK that's also what I thought, but just too hesitant to try. Will try then – Hang Nov 18 '17 at 16:53
0

On OSX, I use the following to get input asynchronously with a time-out..

#include <chrono>
#include <mutex>
#include <string>
#include <iostream>

#if defined(_WIN32) || defined(_WIN64)
#include <windows.h>
#else
#include <pthread.h>
#endif

#if defined(_WIN32) || defined(_WIN64)
#define THREAD_RETURN_TYPE DWORD
#else
#define THREAD_RETURN_TYPE void*
#endif

class semaphore
{
private:
    std::mutex mutex;
    std::condition_variable condition;
    uint32_t count = 0;

public:
    void signal()
    {
        std::unique_lock<std::mutex> lock(mutex);
        ++count;
        condition.notify_one();
    }

    void wait()
    {
        std::unique_lock<std::mutex> lock(mutex);
        condition.wait(lock, [&] {
            return count > 0;
        });
        --count;
    }

    bool try_wait()
    {
        std::unique_lock<std::mutex> lock(mutex);
        if (count > 0)
        {
            --count;
            return true;
        }
        return false;
    }

    template<typename Rep, typename Period>
    bool wait_for(const std::chrono::duration<Rep, Period>& relative_time)
    {
        std::unique_lock<std::mutex> lock(mutex);
        bool finished = condition.wait_for(lock, relative_time, [&] {
            return count > 0;
        });

        if (finished)
        {
            --count;
        }
        return finished;
    }

    template<typename Duration>
    bool wait_until(const std::chrono::time_point<std::chrono::high_resolution_clock, Duration>& absolute_time)
    {
        std::unique_lock<std::mutex> lock(mutex);
        bool finished = condition.wait_until(lock, absolute_time, [&] {
            return count > 0;
        });

        if (finished)
        {
            --count;
        }
        return finished;
    }

    template<typename Clock, typename Duration>
    bool wait_until(const std::chrono::time_point<Clock, Duration>& absolute_time)
    {
        return wait_until(std::chrono::high_resolution_clock::now() + (absolute_time - Clock::now()));
    }
};

class simple_thread
{
private:
    #if defined(_WIN32) || defined(_WIN64)
    HANDLE thread;
    #else
    pthread_t thread;
    #endif

public:
    simple_thread(THREAD_RETURN_TYPE (*threadFunc)(void*), void* args)
    {
        #if defined(_WIN32) || defined(_WIN64)
        thread = CreateThread(0, 0, static_cast<LPTHREAD_START_ROUTINE>(threadFunc), args, 0, 0);
        #else
        pthread_create(&thread, nullptr, threadFunc, args);
        #endif
    }

    void join()
    {
        #if defined(_WIN32) || defined(_WIN64)
        WaitForSingleObject(thread, INFINITE);
        #else
        pthread_join(thread, nullptr);
        #endif
    }

    void detach()
    {
        #if defined(_WIN32) || defined(_WIN64)
        CloseHandle(thread);
        #else
        pthread_detach(thread);
        #endif
    }

    void abort()
    {
        #if defined(_WIN32) || defined(_WIN64)
        TerminateThread(thread, 0);
        #else
        pthread_cancel(thread);
        #endif
    }
};





THREAD_RETURN_TYPE getInput(void* args)
{
    std::string* input = static_cast<std::string*>(static_cast<void**>(args)[0]);
    semaphore* sem = static_cast<semaphore*>(static_cast<void**>(args)[1]);

    getline(std::cin, *input);
    sem->signal();
    return 0;
}

int main()
{
    std::string input;
    semaphore sem;
    void* args[2] = {&input, &sem};

    simple_thread thread(getInput, args);

    if (sem.wait_for(std::chrono::seconds(5)))
    {
        std::cout<<"Input";
        thread.join();
    }
    else
    {
        std::cout<<"Invalid Input";
        thread.abort();
    }
}

You can use pthread to create a semaphore with mutex as well.. or use c++11 like above..

One other thing is that I created my own threading class because I couldn't find a way to kill an std::thread..

Brandon
  • 22,723
  • 11
  • 93
  • 186