Я занимаюсь pthread.
В моей исходной программе он push
es в общую очередь экземпляр класса с именем request
, но сначала я хочу, по крайней мере, убедиться, что я нажимаю что-то в общую очередь.
Это очень простой код, но он просто бросает много ошибок, и я не мог понять причину.
Я предполагаю, что это, вероятно, синтаксис, но все, что я пробовал, не сработало.
Понимаете, почему он не работает?
Ниже приведен код, который я пытался.
extern "C" {
#include<pthread.h>
#include<unistd.h>
}
#include<queue>
#include<iostream>
#include<string>
using namespace std;
class request {
public:
string req;
request(string s) : req(s) {}
};
int n;
queue<request> q;
pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
void * putToQueue(string);
int main ( void ) {
pthread_t t1, t2;
request* ff = new request("First");
request* trd = new request("Third");
int result1 = pthread_create(&t1, NULL, &putToQueue, reinterpret_cast<void*>(&ff));
if (result1 != 0) cout << "error 1" << endl;
int result2 = pthread_create(&t2, NULL, &putToQueue, reinterpret_cast<void*>(&trd));
if (result2 != 0) cout << "error 2" << endl;
pthread_join(t1, NULL);
pthread_join(t2, NULL);
for(int i=0; i<q.size(); ++i) {
cout << q.front().req << " is in queue" << endl;
q.pop();
--n;
}
return 0;
}
void * putToQueue(void* elem) {
pthread_mutex_lock(&mut);
q.push(reinterpret_cast<request>(elem));
++n;
cout << n << " items are in the queue." << endl;
pthread_mutex_unlock(&mut);
return 0;
}
Код ниже содержит комментарии ко всему, что нужно было изменить. Я бы написал подробное описание того, почему они должны были измениться, но я надеюсь, что код говорит сам за себя. Он все еще не пуленепробиваемый. Есть много вещей, которые можно было бы сделать по-другому или лучше (обработка исключений для неудачных new
и т.д.), Но, по крайней мере, она компилирует, запускает и не утечка памяти.
#include <queue>
#include <iostream>
#include <string>
#include <pthread.h>
#include <unistd.h>
using namespace std;
// MINOR: param should be a const-ref
class request {
public:
string req;
request(const string& s) : req(s) {}
};
int n;
queue<request> q;
pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
// FIXED: made protoype a proper pthread-proc signature
void * putToQueue(void*);
int main ( void )
{
pthread_t t1, t2;
// FIXED: made thread param the actual dynamic allocation address
int result1 = pthread_create(&t1, NULL, &putToQueue, new request("First"));
if (result1 != 0) cout << "error 1" << endl;
// FIXED: made thread param the actual dynamic allocation address
int result2 = pthread_create(&t2, NULL, &putToQueue, new request("Third"));
if (result2 != 0) cout << "error 2" << endl;
pthread_join(t1, NULL);
pthread_join(t2, NULL);
// FIXED: was skipping elements because the queue size was shrinking
// with each pop in the while-body.
while (!q.empty())
{
cout << q.front().req << " WAS in queue" << endl;
q.pop();
}
return 0;
}
// FIXED: pretty much a near-total-rewrite
void* putToQueue(void* elem)
{
request *req = static_cast<request*>(elem);
if (pthread_mutex_lock(&mut) == 0)
{
q.push(*req);
cout << ++n << " items are in the queue." << endl;
pthread_mutex_unlock(&mut);
}
delete req; // FIXED: squelched memory leak
return 0;
}
Вывод (может варьироваться)
1 items are in the queue.
2 items are in the queue.
Third WAS in queue
First WAS in queue
Как отмечено в комментарии, я бы посоветовал пропустить прямое использование pthreads и вместо этого использовать примитивы для потоковой передачи С++ 11. Я бы начал с простого класса защищенных очередей:
template <class T, template<class, class> class Container=std::deque>
class p_q {
typedef typename Container<T, std::allocator<T>> container;
typedef typename container::iterator iterator;
container data;
std::mutex m;
public:
void push(T a) {
std::lock_guard<std::mutex> l(m);
data.emplace_back(a);
}
iterator begin() { return data.begin(); }
iterator end() { return data.end(); }
// omitting front() and pop() for now, because they're not used in this code
};
Используя это, основной поток кода остается почти таким же простым и чистым, как однопоточный код, что-то вроде этого:
int main() {
p_q<std::string> q;
auto pusher = [&q](std::string const& a) { q.push(a); };
std::thread t1{ pusher, "First" };
std::thread t2{ pusher, "Second" };
t1.join();
t2.join();
for (auto s : q)
std::cout << s << "\n";
}
Поскольку это стоит прямо сейчас, это многопроцессорная очередь с одним потребителем. Кроме того, это зависит от того, что производители больше не работают, когда происходит потребление. Это правда в этом случае, но не будет/не всегда будет. Если это не так, вам понадобится (малейшая) более сложная очередь, которая блокирует, когда она читает/выскакивает из очереди, а не только при записи на нее.