[英]Using Threads With A Stack (C++)
我尝试使用Threads创建一个堆栈,我的代码:
推送功能(m是互斥)
void Stack::Push(int num){
m.lock();
sta[current++] = num;
m.unlock();
}
流行功能:
int Stack::Pop(){
if (current > 0){
m.lock();
int a = sta[--current];
m.unlock();
return a;
}
return sta[0];
}
主要的:
void threadsPrint(string s){
m1.lock();
cout << s << endl;
m1.unlock();
}
void func(Stack & t){
threadsPrint("T1 Push 1\n");
t.Push(1);
threadsPrint("T1 Push 2\n");
t.Push(2);
threadsPrint("T1 Push 3\n");
t.Push(3);
threadsPrint("T1 Push 4\n");
t.Push(4);
threadsPrint("T1 Push 5\n");
t.Push(5);
threadsPrint("T1 Push 6\n");
t.Push(6);
}
void func2(Stack & t){
threadsPrint("T2 Pop "+to_string(t.Pop())+"\n");
threadsPrint("T2 Pop " + to_string(t.Pop()) + "\n");
threadsPrint("T2 Pop " + to_string(t.Pop()) + "\n");
threadsPrint("T2 Pop " + to_string(t.Pop()) + "\n");
threadsPrint("T2 Pop " + to_string(t.Pop()) + "\n");
threadsPrint("T2 Pop " + to_string(t.Pop()) + "\n");
}
int main(){
Stack t;
thread t1(func,ref(t));
thread t2(func2,ref(t));
t1.join();
t2.join();
return 0;
}
输出:
T1 Push 1
T2 Pop -842150451
T1 Push 2
T2 Pop 1
T1 Push 3
T2 Pop 2
T1 Push 4
T2 Pop 3
T1 Push 5
T2 Pop 4
T1 Push 6
T2 Pop 5
我知道这是错误的代码,但我只是尝试使用线程我仍然没有得到正确的结果,我有什么必须修复代码?
if current > 0){
m.lock();
您无法检查m.lock()
之外的current
。 受竞赛条件限制。
int Stack::Pop(){
m.lock();
int a = current ? sta[--current] : sta[0];
m.unlock();
return a;
}
但是你的堆栈仍然基本上无法区分弹出最后一个项目或弹出空堆栈。 我个人更喜欢这样的东西:
boolean Stack::Pop(int& val){
boolean ret = false;
m.lock();
if (current) {
val = sta[--current];
ret = true;
}
m.unlock();
return ret;
}
当空的时候,等待堆栈增长的问题被委托给调用者。 具有等待和信令的完全成熟的生产者 - 消费者堆栈超出了这里的范围。
当然, lock()
/ unlock()
应该是RAII 。
我认为除了互斥锁之外你可能还想使用一个条件变量,这样如果堆栈上什么都没有,Pop函数会等待某些东西被推送。
在Push()中你可以调用cv.notify_one();
在Pop()中你可以调用cv.wait(m, []{return current > 0;});
请参阅此处的示例: http : //en.cppreference.com/w/cpp/thread/condition_variable
一种可能的交错将给出您看到的前几个值:
T1 T2
threadsPrint("T1 Push 1\n");
threadsPrint("T2 Pop "+to_string(t.Pop())+"\n");
t.Push(1);
当T2执行t.Pop()
你会得到一个垃圾值,T1在推送之前打印输出,所以跟踪是错误的。
要修复跟踪以便更好地反映操作,您需要在锁内部移动跟踪输出(或添加另一个锁)。
要修复“pop-bug”,你需要等到堆栈上有东西才能弹出任何东西。
根据我的观察,代码容易出现竞争条件,即程序的输出取决于其指令的调度。
看看你的代码让我们考虑一下这个调度场景。
第一个线程1被安排并输入func。
打印“T1 Push 1”
在进入推送例程之前,上下文切换到T2。 电流保持为0,没有任何写入。
打印“T2 Pop”并调用pop。 现在做--current会让你访问stack [0],即垃圾值。 如果堆栈是全局的则为零,否则为某个坏值。
上下文切换回t1并继续“T1 Push 2”,依此类推。
因此你得到的输出。 它可能会给出正确的输出几次,但如果调度发生的次数不同,则会产生错误的输出。
所以当你的pop线程遇到当前的0而不是返回stack [0]时,这可能是垃圾,这是明智的,等到什么东西被推入堆栈。
我使用pthreads使用等待和信号机制编写代码。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <iostream>
#include <stack>
using namespace std;
pthread_mutex_t mutex;
pthread_cond_t condv;
pthread_mutexattr_t mattr;
pthread_condattr_t cattr;
int FLAG= 0; //for wait and notify.
int current = 0;
int s[100] ; //stack
void push(int num)
{
pthread_mutex_lock(&mutex);
cout<<"push: curr "<<current<<endl;
s[current++] = num;
FLAG = 1; //Done pushing. Set flag.
pthread_cond_signal(&condv); //notify other thread
pthread_mutex_unlock(&mutex); // release mutex
}
int pop()
{
int a = -1;
pthread_mutex_lock(&mutex);
while(FLAG == 0) {
pthread_cond_wait(&condv,&mutex); //wait until the FLAG is unset.
}
if(current > 0)
{
cout<<"pop: curr "<<current<<endl;
a = s[--current];
if(current == 0) //If you're removing the last element of stack unset FLAG.
FLAG = 0;
pthread_mutex_unlock(&mutex);
return a;
}
pthread_mutex_unlock(&mutex);
return a;
}
void* t1(void *arg)
{
cout<<"pushing 1\n";
push(1);
cout<<"pushing 10\n";
push(10);
cout<<"pushing 12\n";
push(12);
cout<<"pushing 4\n";
push(4);
cout<<"pushing 6\n";
push(6);
cout<<"pushing 7\n";
push(7);
}
void* t2(void *arg)
{
cout<<"Popped "<<pop()<<endl;
cout<<"Popped "<<pop()<<endl;
cout<<"Popped "<<pop()<<endl;
cout<<"Popped "<<pop()<<endl;
cout<<"Popped "<<pop()<<endl;
cout<<"Popped "<<pop()<<endl;
}
int main()
{
pthread_t thread1, thread2;
pthread_mutexattr_init(&mattr);
pthread_mutex_init(&mutex, &mattr);
pthread_condattr_init(&cattr);
pthread_cond_init(&condv, &cattr);
pthread_create(&thread1, NULL, &t1, NULL);
pthread_create(&thread2, NULL, &t2, NULL);
pthread_mutexattr_destroy(&mattr);
pthread_condattr_destroy(&cattr);
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
return 0;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.