Le pattern consumer et producer.
Salut, je vais tenter d'expliquer mon problème :
Je possède deux threads, imaginons que le premier soit le CPU et le second le GPU. (Mais ça pourrait être n'importe quoi)
Ici dans ce cas le GPU joue le rôle de producer et le CPU de consumer.
Tout deux partagent un buffer, alors le but ça serait que le gpu mette à jour une information quelconque du buffer à l'aide d'un programme (le fragment shader par exemple mais ça pourrait être n'importe quel programme) de manière thread safe sans faire attendre le CPU.
Voici un pseudo code que j'ai fait qui utilise boost::lockfree.
Code:
1 2 3 4 5 6 7 8 9 10 11 12
|
boost::lockfree::queue<int> buffer(128);
class Producer {
public :
void operator()(int v) {
buffer.push(v);
}
};
Producer p;
FastDelegate<void> fd(&Producer::operator(), &p, 5);
buffer.consume_one(fd);
return 0; |
Alors déjà là j'ai un soucis c'est que je ne peux pas passer un type classe à la queue, sinon ça ne compile pas, il me dit que la classe n'a pas de destructeur trivial, et que je définisse un destructeur ou pas dans la classe ça ne change rien, on dirait que les queues lock free n'acceptent que des variables de type primitif.
Le problème c'est que je ne peux pas accéder à un élément i du buffer, je suis obligé à chaque fois de push et de pop, donc, je ne sais pas du tout comment faire pour accéder une donnée quelconque de mon buffer et la modifier je pensais faire une classe Task qui contient un std::vector<int> mais comme je ne peux pas utiliser d'autre type que des types primitif..., bref, je ne m'y connais pas beaucoup en programmation multi-threadée et je n'ai pas trouvé beaucoup de doc à se sujet.
Merci pour votre aide.
Multi-Threading. (Valeurs non modifiées et mal lue)
Salut, en fait je possède les classes suivantes :
Task : effectue une instruction.
Producer : modifie une donnée contenue dans un vecteur.
Consumer : copie une donnée dans un conteneur (le résultat d'une instruction si l'instruction renvoie un résultat) pour pouvoir la récupérer ensuite avec un autre thread.
TaskManager : exécute les instructions dans l'ordre dans lequel on lui fournis.
Pour des raison de performance je souhaite utiliser des conteneurs lock-free et non pas des mutex, j'ai donc écris le code source suivant :
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
|
#include <boost/lockfree/queue.hpp>
#include <thread>
class Task {
public :
virtual void operator()() = 0;
unsigned int taskId;
static unsigned int nbTasks;
};
unsigned int Task::nbTasks = 0;
class TaskManager {
public :
TaskManager() : tasks(100) {}
void addTask (Task* task) {
odfaeg::core::FastDelegate<void> command (&Task::operator(), task);
commands.push_back(command);
tasks.push(task);
}
void run() {
while (true) {
if (commands.size() > 0) {
tasks.consume_one(commands.back());
commands.pop_back();
}
}
}
static void addValue(int value) {
std::cout<<"add value : "<<value<<std::endl;
values.push(value);
}
static int getValue() {
int value = -1;
if (values.pop(value))
return value;
std::cout<<"get value : "<<value<<std::endl;
return value;
}
private :
boost::lockfree::queue<Task*> tasks;
std::vector<odfaeg::core::FastDelegate<void>> commands;
static boost::lockfree::queue<int> values;
};
boost::lockfree::queue<int> TaskManager::values (100);
class Producer : public Task {
public :
Producer(std::vector<int>& buffer, int index, int value);
void operator()();
private :
std::vector<int>& buffer;
int index, value;
};
Producer::Producer(std::vector<int>& buffer, int index, int value) :
buffer(buffer), index(index), value(value) {
taskId = nbTasks;
nbTasks++;
}
void Producer::operator()() {
buffer[index] = value;
}
class Consumer : public Task {
public :
Consumer(std::vector<int>& buffer, int index);
void operator()();
private :
std::vector<int>& buffer;
int index, value;
};
Consumer::Consumer(std::vector<int>& buffer, int index) :
buffer(buffer), index(index) {
taskId = nbTasks;
nbTasks++;
}
void Consumer::operator()() {
value = buffer[index];
TaskManager::addValue(value);
}
using namespace odfaeg::core;
int main()
{
std::vector<int> buffer = {0, 1, 2, 3, 4, 5};
TaskManager tm;
Task* t1 = new Producer(buffer, 0, 6);
Task* t2 = new Consumer(buffer, 0);
tm.addTask(t1);
tm.addTask(t2);
std::thread t(&TaskManager::run, &tm);
int value;
while (TaskManager::getValue() == -1) {
}
value = TaskManager::getValue();
std::cout<<"final value : "<<value<<std::endl;
t.join();
delete t1;
delete t2;
return 0;
} |
Malheureusement, cela ne marche pas, la valeur retournée par TaskManager est toujours -1. :/
Et les valeurs dans mon vecteur ne sont pas modifiée non plus. :/
PS : je n'utilise pas de librairie de multi-threading côté GPU pour l'instant, j'essaye de faire des tests avec le CPU et la librairie standart du c++14 ainsi que boost.