0

I am using libcurl to do url request, the network latency is not acceptable. so i want it to be non blocking.

the bad news is libcurl non-blocking method is very confusing, my ideal api function will be curl_perform(CRUL*, int), the int control blocking or non-blocking.

but libcurl seems need to use select to manage those url request.

in my understanding, select need loop to monitor the socket, which need a single thread, and this thread can't do anything else.

so, the other method is:

I create a shared queue, and open another thread, this thread is while(1) { check_queue(); }

when the queue is not empty, it handles the data.

so, my main thread just put the request into queue, then it can do other things.

what is the difference between select and another thread?

nothingisme
  • 119
  • 5
  • The primary purpose of `select()` is to allow a single thread to monitor the I/O status of multiple sockets simultaneously, so that a single thread can respond to events on any of the (potentially large number of) sockets it is monitoring. So it's probably untrue that "this thread can't do anything else". – Jeremy Friesner May 31 '23 at 02:35
  • Never us busy loops to let a thread wait! When two threads need to synchronize on some state/value use [condition variables](https://en.cppreference.com/w/cpp/thread/condition_variable) which despite their name are more like interthread signals (like hey wakeup something might have changed). But do read this too : [the traps of condition variables](https://www.modernescpp.com/index.php/c-core-guidelines-be-aware-of-the-traps-of-condition-variables) – Pepijn Kramer May 31 '23 at 03:17

1 Answers1

0

Here is a code example for you (live demo : https://onlinegdb.com/lrYaY1osw)

#include <chrono>
#include <iostream>
#include <thread>
#include <condition_variable>
#include <mutex>

// a condition variable is (almost always used) in combination 
// with a mutex and a value so wrap that concept once for reuse 
// it is a template so you can also use it with enum values
// (very useful for communicating state changes!)
// or you can use integer type to use it as a semaphore too

template<typename type_t>
class synchronization_signal_t final
{
public:
    explicit synchronization_signal_t(const type_t& value) :
        m_value{ value }
    {
    }

    ~synchronization_signal_t() = default;

    void operator=(const type_t& value)
    {
        std::unique_lock<std::mutex> lock{ m_mtx };
        m_value = value;
        m_signal.notify_all();
    }

    void wait_for(const type_t& value)
    {
        std::unique_lock<std::mutex> lock{ m_mtx };
        // wait with predicate
        m_signal.wait(lock, [&] { return m_value == value; });
    }

private:
    std::mutex m_mtx;
    std::condition_variable m_signal;
    type_t m_value;
};

// for readable time notation
using namespace std::chrono_literals;

int main()
{
    // create a simple two state (bool) signal
    synchronization_signal_t<bool> signal{ false };

    // start a thread
    std::thread thread1{ [&]
    {
      std::cout << "thread started\n";
      std::this_thread::sleep_for(2s);
      std::cout << "thread setting signal\n";

      // set the signal
      signal = true;
      
      std::cout << "thread signal set\n";
    }};

    // the mainthread will wait for thread1 to have finished
    std::cout << "mainthread waiting...\n";
    signal.wait_for(true);
    std::cout << "mainthread done waiting\n";

    thread1.join();
    return 0;
}
Pepijn Kramer
  • 9,356
  • 2
  • 8
  • 19