13
Apr
2013

C++11 Ping-Pong std::threads

Como Tarea 1 de programación en el curso Pattern-Oriented Architectures for Concurrent and Networked Software (POSA, para los amigos) que estoy haciendo en Coursera, se requirió hacer un programa sencillo de ping-pong entre threads. El objetivo era poner en práctica algunas primitivas para el manejo de la concurrencia y la sincronización. Básicamente el output por consola de este programa debía ser algo así:
Ready… Set… Go!
Ping!
Pong!
Ping!
Pong!
Ping!
Pong!
Done!

Se podían elegir muchos lenguajes para el desarrollo. Antes de pasar a lo técnico, quiero mostrar algunas cifras relativas al desarrollo del curso. Están inscriptas unas 30.000 personas apróx. Esta tarea (correspondiente a la 4ta semana del curso), fue entregada por 1.600 personas apróx. Esta fue la cantidad de programas por lenguaje:

Java 840
C++11 260
C# 199
Python 170
Ruby 56
C++ ACE 51
Scala 50
C++ boost 4

Personalmente elegí C++11. Mi solución está armada en un estilo task-dispatch. Hay un thread de control que envía las tasks a una cola de mensajes y otro thread procesador que toma estas tasks y las ejecuta. Aquí va el código con las diferentes primitivas de concurrencia y sincronización:

//============================================================================
// Name        : Program.cc
// Author      : -
// Version     : -
// Copyright   : -
// Description : Programming_Assigment_1 - POSA - Coursera
// Compiler	   : gcc version 4.7.2 20121109 (Red Hat 4.7.2-8) (GCC)
// Stdlib      : libstdc++.so.6.0.17
// Compile command: g++  -o a.out  Program.cc  -lpthread -std=c++11
//============================================================================
 
#include <iostream>
#include
<thread>
#include <mutex>
#include <future>
#include <deque>
#include
<list>
#include <memory>
#include <cstdlib>
#include <ctime>
 
//Global variables
std::mutex tasks_list_mutex;
std::deque<std::packaged_task<void(void)>> tasks_list;
std::mutex worker_waiting_for_task_mutex;
std::condition_variable worker_waiting_for_task;
std::mutex task_processor_finished_mutex;
std::condition_variable task_processor_finished;
const double prob_finish = 0.1;
 
//Global functions defined
void task_processor(void);
void add_start(void);
void add_ping(void);
void add_pong(void);
void add_done(void);
void start(void);
void ping(void);
void pong(void);
void done(void);
 
int main() {
	std::srand(std::time(0));
 
	//Initialize the worker
	std::thread worker(task_processor);
	worker.detach();
 
	//Initialize the repetitive actions sequence
	std::list<void(*)(void)> repetitive_task_generators_list;
	repetitive_task_generators_list.push_back(add_ping);
	repetitive_task_generators_list.push_back(add_pong);
 
	//Add a start task!
	add_start();
 
	// Run the main loop
	std::list<void(*)(void)>::iterator tasks_generators_iterator = repetitive_task_generators_list.begin();
	for(;;){
		//Add task from the repetitive sequence and iterate to the next
		(*tasks_generators_iterator)();
		tasks_generators_iterator++;
		if(tasks_generators_iterator == repetitive_task_generators_list.end()){
			tasks_generators_iterator = repetitive_task_generators_list.begin();
		}
 
		//Decide if adding a finishing task
		if(std::rand() < (int)(((double)RAND_MAX)*prob_finish)){
			add_done();
			break;
		}
	}
 
	std::unique_lock<std::mutex> task_processor_finished_lock(task_processor_finished_mutex);
	task_processor_finished.wait(task_processor_finished_lock);
	return 0;
}
 
void task_processor(void){
	for(;;){
		while(!tasks_list.empty()){
			std::unique_lock<std::mutex> lock_tasks_list(tasks_list_mutex);
			std::packaged_task<void(void)> task(std::move(tasks_list.front()));
			tasks_list.pop_front();
			lock_tasks_list.unlock();
			task();
		}
		std::unique_lock<std::mutex> lock_worker_waiting_for_task(worker_waiting_for_task_mutex);
		worker_waiting_for_task.wait(lock_worker_waiting_for_task);
	}
}
 
void add_start(void){
	std::unique_lock<std::mutex> lock_tasks_list(tasks_list_mutex);
	tasks_list.push_back(std::packaged_task<void(void)>(&start));
	worker_waiting_for_task.notify_one();
}
 
void add_ping(void){
	std::unique_lock<std::mutex> lock_tasks_list(tasks_list_mutex);
	tasks_list.push_back(std::packaged_task<void(void)>(&ping));
	worker_waiting_for_task.notify_one();
}
 
void add_pong(void){
	std::unique_lock<std::mutex> lock_tasks_list(tasks_list_mutex);
	tasks_list.push_back(std::packaged_task<void(void)>(&pong));
	worker_waiting_for_task.notify_one();
}
 
void add_done(void){
	std::unique_lock<std::mutex> lock_tasks_list(tasks_list_mutex);
	tasks_list.push_back(std::packaged_task<void(void)>(&done));
	worker_waiting_for_task.notify_one();
}
 
void start(void){
	std::cout << "Ready… Set… Go!" << std::endl;
}
 
void ping(void){
	std::cout << "Ping!" << std::endl;
}
 
void pong(void){
	std::cout << "Pong!" << std::endl;
}
 
void done(void){
	std::cout << "Done!" << std::endl;
	task_processor_finished.notify_one();
}
Escribir un comentario