Q:

Inter thread communication

Here is my simple code, I want to get in the console_task, the value of the variable i in the dialer_task... without using a global variable.
#include <stdio.h>
#include <sys/types.h>
#include <signal.h>
#include <strings.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <thread>
#include "console.hpp"

using namespace std;

void console_task(){
    console();
}

void dialer_task(){
    int i=0;
    while (1) {
        printf("LOOP %d\n",i);
        i++;
        sleep(5);
    }
}

int main()
{
    thread t1(console_task);
    thread t2(dialer_task);

    t1.join();
    t2.join();
    return 0;
}

A:

The constraint that there may not be a global variable to share the state between the threads leaves essentially 2 viable alternatives;

Allocate the shared state on the heap and pass that on to the threads
Allocate the shared state on the original thread's stack and "feed" it to the worker threads for shared use.

The catch to both solutions is to make sure that the access is appropriately guarded or atomic.
A simple solution is to use an std::atomic and share the reference between the threads.
#include <type_traits>
#include <thread>
#include <atomic>
#include <iostream>

void console_task(std::atomic_int& j) {
    using namespace std;

    int i = 0;
    while (++i < 50) {
        cout << "task " << j << endl; // uncontrolled access to the console (demo)
        std::chrono::microseconds delay{50};
        this_thread::sleep_for(delay);
    }
}

void dialer_task(std::atomic_int& j){
    using namespace std;
    int i = 0;
    while ( ++i < 10) {
        //cout << "LOOP " << i << endl;  // uncontrolled access to the console (demo)
        std::chrono::microseconds delay{145};
        this_thread::sleep_for(delay);
        j = i;
    }
}

int main()
{
    std::atomic_int i { 0 };
    std::thread t1( console_task, std::ref(i));
    // a lambda with reference capture could also be used
    // std::thread t1( [&](){console_task(i);} );
    std::thread t2( dialer_task, std::ref(i));

    t1.join();
    t2.join();
    return 0;
}

There is a catch to the shared atomic, it needs to remain valid for the duration of the threads (as it does here).
Demo code.
Further heap based alternatives can be considered; e.g. using a shared std::mutex together with a std::shared_ptr.

