// // // // // Lab. Calcolo II - Esempio di codice // // // // // // // // Example code: std::thread demonstration: the dining philosophers // (20130913 francesco.prelz@mi.infn.it) #include <iostream> #include <string> #include <sstream> #include <iomanip> #include <vector> #include <list> #include <memory> #include <chrono> // Work around the fact that the sleep functions aren't included in the // default build of the GNU libstdc++ #ifndef _GLIBCXX_USE_NANOSLEEP #define _GLIBCXX_USE_NANOSLEEP #endif #include <thread> #include <mutex> #include <condition_variable> #include <ctime> // localtime, strftime #include "TApplication.h" #include "TCanvas.h" #include "TMultiGraph.h" #include "TGraph.h" #include "TAxis.h" std::vector<std::shared_ptr<std::mutex>> forks; std::mutex waiting_list_mutex; const int course_time_sec = 2; struct philosopher { std::string name; int right_fork, left_fork; unsigned int n_courses; std::shared_ptr<std::thread> thread; std::vector<std::chrono::system_clock::time_point> start_times; std::vector<std::chrono::system_clock::time_point> end_times; std::shared_ptr<std::condition_variable> wakeup; void have_meal(); // Object must be callable. void operator()() {have_meal();} }; std::list<philosopher *> waiting_list; std::string time_label() { std::chrono::system_clock::time_point lab_time = std::chrono::system_clock::now(); char timelabel[30]; tm ltime; time_t lab_time_t = std::chrono::system_clock::to_time_t(lab_time); ::localtime_r(&lab_time_t, &ltime); std::ostringstream ret; if (strftime(timelabel, sizeof(timelabel), "%T", &ltime) > 0) { int usec = std::chrono::duration_cast<std::chrono::microseconds>(lab_time.time_since_epoch()).count() - lab_time_t*1000000LL; ret << timelabel << "." << std::setprecision(6) << usec << ": "; } return ret.str(); } void philosopher::have_meal() { philosopher *p = this; for (unsigned int i = 1; i <= p->n_courses; ) { while (!forks[p->right_fork]->try_lock()) { std::unique_lock<std::mutex> wlock(waiting_list_mutex); waiting_list.push_back(p); p->wakeup->wait(wlock); } std::cout << time_label() << "Philosopher " << p->name << " took fork # " << p->right_fork << std::endl; if (!forks[p->left_fork]->try_lock()) { forks[p->right_fork]->unlock(); std::cout << time_label() << "Philosopher " << p->name << " returned fork # " << p->right_fork << std::endl; std::unique_lock<std::mutex> wlock(waiting_list_mutex); // Try waking up the first philosopher in line if (waiting_list.size() > 0) { waiting_list.front()->wakeup->notify_one(); waiting_list.pop_front(); } waiting_list.push_back(p); p->wakeup->wait(wlock); continue; } std::cout << time_label() << "Philosopher " << p->name << " took fork # " << p->left_fork << std::endl; std::cout << time_label() << "Philosopher " << p->name << " enjoying course # " << i << std::endl; p->start_times.push_back(std::chrono::system_clock::now()); std::this_thread::sleep_for(std::chrono::seconds(course_time_sec)); forks[p->right_fork]->unlock(); forks[p->left_fork]->unlock(); p->end_times.push_back(std::chrono::system_clock::now()); std::cout << time_label() << "Philosopher " << p->name << " returned forks # " << p->right_fork << " and " << p->left_fork << std::endl; { // Try waking up the first two philosophers in line std::unique_lock<std::mutex> wlock(waiting_list_mutex); if (waiting_list.size() > 1) { waiting_list.front()->wakeup->notify_one(); waiting_list.pop_front(); } if (waiting_list.size() > 0) { waiting_list.front()->wakeup->notify_one(); waiting_list.pop_front(); } #ifndef GREEDY_PHILOSOPHER bool need_to_wait = false; // Put ourselves at the back of the waiting list // unless it's empty (we could be deadlocking ourselves out) if ((i <= p->n_courses) && (waiting_list.size() > 0)) { waiting_list.push_back(p); need_to_wait = true; } if (need_to_wait) p->wakeup->wait(wlock); #endif } #ifndef GREEDY_PHILOSOPHER // Give the other threads a chance to grab our forks std::this_thread::sleep_for(std::chrono::milliseconds(1)); #endif i++; } std::cout << time_label() << "Philosopher " << p->name << " finished meal." << std::endl; } int main (int argc, char *argv[]) { const unsigned int n_philosophers=6; philosopher the_philosophers[n_philosophers]; const char *names[n_philosophers] = {"Plato", "Aristotle", "Descartes", "Kant", "Hegel", "Heidegger"}; forks.resize(n_philosophers); for (unsigned int i = 0; i<n_philosophers; ++i) { forks[i].reset(new std::mutex()); } for (unsigned int i = 0; i<n_philosophers; ++i) { the_philosophers[i].name = names[i]; the_philosophers[i].left_fork = (i + n_philosophers - 1) % n_philosophers; the_philosophers[i].right_fork = i; the_philosophers[i].n_courses = 3; the_philosophers[i].wakeup.reset(new std::condition_variable()); the_philosophers[i].thread.reset(new std::thread(std::ref(the_philosophers[i]))); if (the_philosophers[i].thread == 0) { std::cerr << argv[0] << ": Error creating thread # " << i << std::endl; return 1; } } // Wait for the threads to finish. for (unsigned int i = 0; i<n_philosophers; ++i) { the_philosophers[i].thread->join(); } // Build a timechart of what happened TMultiGraph lines; for (unsigned int i = 0; i<n_philosophers; ++i) { for (unsigned int j = 0; j<the_philosophers[i].n_courses; ++j) { if (the_philosophers[i].start_times.size() < (j+1)) break; if (the_philosophers[i].end_times.size() < (j+1)) break; double x[2],y[2]; y[0] = y[1] = static_cast<double>(i+1); x[0] = static_cast<double>(std::chrono::duration_cast<std::chrono::microseconds>(the_philosophers[i].start_times[j].time_since_epoch()).count()) / 1000000.; x[1] = static_cast<double>(std::chrono::duration_cast<std::chrono::microseconds>(the_philosophers[i].end_times[j].time_since_epoch()).count()) / 1000000.; // The TMultiGraph object will take care of deallocating // heap graphs. TGraph *new_line = new TGraph(2, x, y); new_line->SetLineColor(j+2); new_line->SetLineWidth(20); lines.Add(new_line, "L"); } } TApplication theApp("The Dining Philosophers", &argc, argv); TCanvas c1("c1","Timeline", 800, 500); // No axis is found unless the multigraph is drawn once first. lines.Draw("A"); TAxis *ax = lines.GetXaxis(); if (ax) { ax->SetTimeDisplay(1); ax->SetTimeFormat("%H:%M:%S"); } lines.Draw("A"); std::cout << "*** Exit the program by selecting Quit from the File menu ***" << std::endl; theApp.Run(kTRUE); return 0; }