Le cas 1, « calculs concurrents, couplage plus faible », tel que proposé dans vos notes de cours, se présente comme suit.
struct Probleme
{
virtual bool est_resolu() const volatile = 0;
virtual void resoudre_un() volatile = 0;
virtual ~Probleme() throw()
{ }
};
unsigned long __stdcall calcul(void *p)
{
for (Probleme *prob = static_cast<Probleme *>(p);
!prob->est_resolu(); )
prob->resoudre_un();
return 0UL;
}
#include <algorithm>
class Navire
{
public:
typedef short poids_t;
enum { NB_SOUTES = 20 };
private:
poids_t soutes_[NB_SOUTES];
int no_;
bool complete_, en_cours_;
public:
int numero() const throw()
{ return no_; }
bool operator<(const Navire &n) const throw()
{ return numero() < n.numero(); }
Navire(const int no)
: no_(no), complete_(false), en_cours_(false)
{
using std::fill;
fill(begin(), end(), poids_t());
}
void placer(const poids_t p)
{
using std::min_element;
debut_travail();
*(min_element(begin(), end())) += p;
complete_ = true;
}
void debut_travail() throw()
{ en_cours_ = true; }
bool est_place() const throw()
{ return complete_; }
bool est_en_cours() const throw()
{ return en_cours_; }
bool pas_en_cours() const throw()
{ return !en_cours_; }
typedef poids_t* iterator;
typedef const poids_t* const_iterator;
iterator begin() throw()
{ return soutes_; }
const_iterator begin() const throw()
{ return soutes_; }
iterator end() throw()
{ return std::end(soutes_); }
const_iterator end() const throw()
{ return std::end(soutes_); }
};
template <class T>
class Sequence
{
T cur_;
public:
Sequence(T base)
: cur_(base)
{
}
T operator()()
{ return cur_++; }
};
template <class T>
Sequence<T> creer_sequence(T init)
{ return Sequence<T>(init); }
//#include "Mutex.h"
//#include "Incopiable.h"
#include <windows.h>
//#include "Mutex.h"
class Incopiable
{
Incopiable(const Incopiable &);
void operator=(const Incopiable &);
protected:
Incopiable() throw() {}
~Incopiable() throw() {}
};
class Mutex
{
HANDLE m_;
public:
Mutex() : m_(CreateMutex(0, FALSE, 0)) {}
~Mutex() throw() { CloseHandle(m_); }
void obtenir() const volatile throw() { WaitForSingleObject(m_, INFINITE); }
void relacher() const volatile throw() { ReleaseMutex(m_); }
};
class Autoverrou
{
const volatile Mutex &m_;
public:
Autoverrou(const volatile Mutex &m) throw() : m_(m) { m_.obtenir(); }
~Autoverrou() throw() { m_.relacher(); }
};
#include <vector>
#include <algorithm>
#include <cstdlib>
#include <ctime>
#include <iterator>
class Chargement
: Incopiable, public Probleme
{
typedef std::vector<Navire> vnav;
public:
typedef Navire::poids_t poids_t;
enum { NB_NAVIRES = 1000 };
private:
Mutex mutex_;
vnav navires_;
class ACharger
{
std::vector<poids_t> cargo_;
ACharger()
{
using std::srand;
using std::time;
using std::generate_n;
srand(static_cast<unsigned int>(time(0)));
const int NELEMS = 100000;
generate_n(back_inserter(cargo_), NELEMS, []() {
return static_cast<poids_t>(rand() % 40 + 10);
});
}
public:
typedef std::vector<poids_t>::iterator iterator;
typedef std::vector<poids_t>::const_iterator const_iterator;
static ACharger &get()
{
static ACharger singleton;
return singleton;
}
iterator begin() throw()
{ return std::begin(cargo_); }
const_iterator begin() const throw()
{ return std::begin(cargo_); }
iterator end() throw()
{ return std::end(cargo_); }
const_iterator end() const throw()
{ return std::end(cargo_); }
};
bool est_resolu() const
{
using std::count_if;
return count_if(
std::begin(navires_), std::end(navires_), [](const Navire &nav) {
return nav.est_place();
}) == navires_.size();
}
void resoudre_un()
{
using std::find_if;
using std::for_each;
auto itt = find_if(navires_.begin(),navires_.end(), [](const Navire &nav) {
return nav.pas_en_cours();
});
if (itt == std::end(navires_)) return;
itt->debut_travail();
for_each(std::begin(ACharger::get()), std::end(ACharger::get()), [&itt](const poids_t poids) {
itt->placer(poids);
});
}
public:
Chargement()
{
using std::generate_n;
using std::back_inserter;
int n = NB_NAVIRES;
generate_n(back_inserter<vnav>(navires_), n, creer_sequence(0));
}
bool est_resolu() const volatile
{
Autoverrou av(mutex_);
return const_cast<const Chargement*>(this)->est_resolu();
}
void resoudre_un() volatile
{
Autoverrou av(mutex_);
const_cast<Chargement*>(this)->resoudre_un();
}
std::vector<Navire> resultats() const
{ return navires_; }
};
#include <iostream>
#include <ctime>
#include <windows.h>
int main()
{
using std::begin;
using std::end;
using std::cout;
using std::endl;
using std::clock;
using std::clock_t;
using std::vector;
using std::for_each;
const int NTHREADS = 10;
for (int n = 1; n <= NTHREADS; ++n)
{
Chargement cn;
vector<HANDLE> v(n);
for (int i = 0; i < n; ++i)
v[i] = CreateThread(0, 0, calcul, &cn, CREATE_SUSPENDED, 0);
clock_t avant = clock();
for_each(begin(v), end(v), ResumeThread);
WaitForMultipleObjects(n, &v[0], TRUE, INFINITE);
clock_t apres = clock();
for_each(begin(v), end(v), CloseHandle);
cout << "Temps total, "
<< Chargement::NB_NAVIRES << " navires, "
<< Navire::NB_SOUTES << " soutes, "
<< n << " thread(s): "
<< static_cast<double>(apres-avant) /
CLOCKS_PER_SEC * 1000
<< " ms." << endl;
}
}
Sur mon petit ordinateur portatif, compilé avec Visual Studio 2010, configuration Release, et en prenant bien entendu soin d'ajouter _SECURE_SCL=0 aux options du préprocesseur pour désactiver les (franchement inutiles et nuisibles) Checked Iterators, j'obtiens :
Temps total, 1000 navires, 20 soutes, 1 thread(s): 7978 ms.
Temps total, 1000 navires, 20 soutes, 2 thread(s): 7581 ms.
Temps total, 1000 navires, 20 soutes, 3 thread(s): 7645 ms.
Temps total, 1000 navires, 20 soutes, 4 thread(s): 7653 ms.
Temps total, 1000 navires, 20 soutes, 5 thread(s): 7519 ms.
Temps total, 1000 navires, 20 soutes, 6 thread(s): 7685 ms.
Temps total, 1000 navires, 20 soutes, 7 thread(s): 7440 ms.
Temps total, 1000 navires, 20 soutes, 8 thread(s): 7299 ms.
Temps total, 1000 navires, 20 soutes, 9 thread(s): 7521 ms.
Temps total, 1000 navires, 20 soutes, 10 thread(s): 7581 ms.
Appuyez sur une touche pour continuer...