Tag Archives: Threads

How to create, launch, and join a thread in C++

Method 1: Boost.Thread

#include <iostream>
#include <string>
#include <boost/thread.hpp>

class Hello
{
public:
	void operator()(const std::string& msg) const
	{
		std::cout << msg << "\n";
	}
};

int main()
{
	boost::thread t = boost::thread(Hello(), "Hello");
	t.join();
}

Method 2: pthreads (POSIX)

#include <iostream>

#include <pthread.h>

void* hello(void *msg)
{
    std::cout << static_cast<const char*>(msg) << "\n";
    return NULL;
}

int main()
{
    pthread_t thread;
    pthread_attr_t attr;
	pthread_attr_init(&attr);
	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
    char msg[] = "Hello";
	int status = pthread_create(&thread, &attr, hello, msg);
	pthread_attr_destroy(&attr);
	if (status != 0) {
        std::cerr << "Failed to create thread\n";
        return 1;
	}
    status = pthread_join(thread, NULL);
    if (status != 0) {
        std::cerr << "Failed to join thread\n";
        return 1;
    }
}

Method 3: Windows threads

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif

#include <Windows.h>
#include <process.h>

#include <iostream>

void hello(void *msg)
{
	std::cout << static_cast<const char*>(msg) << "\n";
}

int main()
{
	char msg[] = "Hello";
	HANDLE thread = reinterpret_cast<HANDLE>(_beginthread(hello, 0, msg));
	if (thread == INVALID_HANDLE_VALUE) {
		std::cerr << "Failed to create thread\n";
		return 1;
	}
	WaitForSingleObject(thread, INFINITE);
}

Method 4: std::thread (C++11)

#include <string>
#include <iostream>
#include <thread>

void hello(const std::string& msg)
{
    std::cout << msg << "\n";
}

int main()
{
    std::thread t(hello, "Hello");
    t.join();
}

Promises and futures

The words "promise" and "future" have historically been used rather interchangeably in concurrent programming, but they have very particular meanings in C++.

A promise is an asynchronous provider – it provides a result to a shared state
A future is an asynchronous return object – it reads a result from a shared state, waiting for it if necessary

  1. You construct a promise of type T, and you are creating a shared state that can store a variable of type T
  2. You can get a future from this promise with get_future()
  3. You can set the value of the promise’s variable with set_value()
  4. The future can read the value of the variable when it becomes available with its get() method

Below is a simple example of creating a promise, getting a future from it, sending the future to a thread, setting the value of the promise, and finally retrieving it from the future.

#include <iostream>
#include <functional>
#include <thread>
#include <future>

template <typename T>
void print (std::future<T>& fut) {
    T x = fut.get();
    std::cout << "value: " << x << "\n";
}

int main()
{
    std::promise<int> prom;
    std::future<int> fut = prom.get_future();
    std::thread thr (print<int>, std::ref(fut));
    prom.set_value(10);
    thr.join();
}

Threaded server for Windows

This is the Windows version of the Threaded Server.

/* 
 *  Link with Ws2_32.lib
 */

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif

#include <stdio.h>
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <process.h> /* _beginthread() */

#define PORT    "32001" /* Port to listen on */
#define BACKLOG 10		/* Passed to listen() */

void handle(void *pParam)
{
	/* send(), recv(), closesocket() */

    free(pParam);
}

int main(void)
{
	WORD wVersion = MAKEWORD(2, 2);
	WSADATA wsaData;
	int iResult;
	SOCKET sock;
	struct addrinfo hints, *res;
	int reuseaddr = 1; /* True */

	/* Initialise Winsock */
	if ((iResult = WSAStartup(wVersion, &wsaData)) != 0) {
		printf("WSAStartup failed: %d\n", iResult);
		return 1;
	}

	/* Get the address info */
	ZeroMemory(&hints, sizeof hints);
	hints.ai_family = AF_INET;
	hints.ai_socktype = SOCK_STREAM;
	if (getaddrinfo(NULL, PORT, &hints, &res) != 0) {
		perror("getaddrinfo");
		return 1;
	}

	/* Create the socket */
	sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
	if (sock == INVALID_SOCKET) {
		perror("socket");
		WSACleanup();
		return 1;
	}

	/* Enable the socket to reuse the address */
	if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&reuseaddr, 
				sizeof(int)) == SOCKET_ERROR) {
		perror("setsockopt");
		WSACleanup();
		return 1;
	}

	/* Bind to the address */
	if (bind(sock, res->ai_addr, res->ai_addrlen) == SOCKET_ERROR) {
		perror("bind");
		WSACleanup();
		return 1;
	}

	freeaddrinfo(res);

	/* Listen */
	if (listen(sock, BACKLOG) == SOCKET_ERROR) {
		perror("listen");
		WSACleanup();
		return 1;
	}

	/* Main loop */
	while(1) {
		size_t size = sizeof(struct sockaddr);
		struct sockaddr_in their_addr;
		SOCKET newsock;

		ZeroMemory(&their_addr, sizeof (struct sockaddr));
		newsock = accept(sock, (struct sockaddr*)&their_addr, &size);
		if (newsock == INVALID_SOCKET) {
			perror("accept\n");
		}
		else {
			/* Use the new socket */
			uintptr_t thread;
			printf("Got a connection from %s on port %d\n", 
				inet_ntoa(their_addr.sin_addr), ntohs(their_addr.sin_port));
			printf("New socket is %d\n", newsock);
            /* Make a safe copy of newsock */
            int *safesock = malloc(sizeof(int));
            if (safesock) {
                *safesock = newsock;
                thread = _beginthread(handle, 0, safesock);
                if (thread == -1) {
                    fprintf(stderr, "Couldn't create thread: %d\n", GetLastError());
                    closesocket(newsock);
                }
            }
            else {
                perror("malloc");
            }
		}
	}

	/* Clean up */
	closesocket(sock);
	WSACleanup();

	return 0;
}

Threaded server

This server creates a new thread for each client connection. This also permits as many connections as resources will allow. It is less resource intensive than forking.

It is the only option for multiprocessing on Windows, and on Linux is best suited to server computers with more than 2 cores.

When using multiple threads it is necessary to use synchronisation locks when accessing any shared application state, and also when calling many socket API functions.

/* 
 *  A threaded server
 *  by Martin Broadhurst (www.martinbroadhurst.com)
 *  Compile with -pthread
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h> /* memset() */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>
#include <pthread.h>

#define PORT    "32001" /* Port to listen on */
#define BACKLOG     10  /* Passed to listen() */

void *handle(void *pnewsock)
{
	/* send(), recv(), close() */

    free(pnewsock);

	return NULL;
}

int main(void)
{
    int sock;
	pthread_t thread;
	struct addrinfo hints, *res;
	int reuseaddr = 1; /* True */

	/* Get the address info */
	memset(&hints, 0, sizeof hints);
	hints.ai_family = AF_INET;
	hints.ai_socktype = SOCK_STREAM;
	if (getaddrinfo(NULL, PORT, &hints, &res) != 0) {
		perror("getaddrinfo");
		return 1;
	}

	/* Create the socket */
    sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
	if (sock == -1) {
		perror("socket");
		return 1;
	}

	/* Enable the socket to reuse the address */
	if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(int)) == -1) {
		perror("setsockopt");
		return 1;
	}

	/* Bind to the address */
    if (bind(sock, res->ai_addr, res->ai_addrlen) == -1) {
        perror("bind");
        return 0;
    }

	freeaddrinfo(res);

	/* Listen */
    if (listen(sock, BACKLOG) == -1) {
        perror("listen");
        return 0;
    }

	/* Main loop */
	while (1) {
	    socklen_t size = sizeof(struct sockaddr_in);
		struct sockaddr_in their_addr;
		int newsock = accept(sock, (struct sockaddr*)&their_addr, &size);
		if (newsock == -1) {
			perror("accept");
		}
		else {
			printf("Got a connection from %s on port %d\n", 
					inet_ntoa(their_addr.sin_addr), htons(their_addr.sin_port));
            /* Make a safe copy of newsock */
            int *safesock = malloc(sizeof(int));
            if (safesock) {
                *safesock = newsock;
                if (pthread_create(&thread, NULL, handle, safesock) != 0) {
                    fprintf(stderr, "Failed to create thread\n");
                }
            }
            else {
                perror("malloc");
            }
		}
	}

    close(sock);

    return 0;
}