使用带堆栈的线程(C++)
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 1n");
t.Push(1);
threadsPrint("T1 Push 2n");
t.Push(2);
threadsPrint("T1 Push 3n");
t.Push(3);
threadsPrint("T1 Push 4n");
t.Push(4);
threadsPrint("T1 Push 5n");
t.Push(5);
threadsPrint("T1 Push 6n");
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 1n");
threadsPrint("T2 Pop "+to_string(t.Pop())+"n");
t.Push(1);
当T2执行t.Pop()
时,您将得到一个垃圾值,而T1在推送之前打印了它的输出,因此跟踪是错误的。
要修复跟踪以便更好地反映操作,您需要将跟踪输出移动到锁内(或添加另一个锁)
要修复"弹出错误",您需要等到堆栈上有内容后再弹出任何内容。
根据我的观察,代码容易出现竞争条件,即程序的输出取决于指令的调度。
看看您的代码,让我们考虑一下这个调度场景。
第一个线程1被调度,它进入func。
打印"T1推送1"
上下文在进入推送例程之前切换到T2。现在的保持0并且没有任何内容写入。
打印"T2 Pop"并呼叫Pop。现在做--current将使您访问堆栈[0],即垃圾值。如果堆栈是全局的,则为零,否则为某个错误值。
上下文切换回t1并继续"t1推送2",依此类推
因此,你得到的输出。它可能会多次给出正确的输出,但如果调度发生不同,则会给出错误的输出。
因此,当您的pop线程到达当前的0而不是返回堆栈[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 1n";
push(1);
cout<<"pushing 10n";
push(10);
cout<<"pushing 12n";
push(12);
cout<<"pushing 4n";
push(4);
cout<<"pushing 6n";
push(6);
cout<<"pushing 7n";
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;
}
- 从不同线程使用int64的不同字节安全吗
- 删除一个线程上有数百万个字符串的大型哈希映射会影响另一个线程的性能
- 在C++中使用cURL和多线程
- 为什么我的C#代码在调用回C++COM直到Task时会暂停.等待/线程.加入
- 在cuda线程之间共享大量常量数据
- 如何将元素添加到数组的线程安全函数?
- 线程,如果else语句,都是错误的上下文切换后,会发生什么
- C++Boost Asio Pool线程,带有lambda函数和传递引用变量
- Qt C++静态thread_local QNetworkAccessManager是线程应用程序的好选择吗
- 异常属于C++中的线程还是进程
- C++中的线程安全删除
- C++使用params创建线程函数会导致转换错误
- 类与私有变量的其他类之间的线程安全性
- CoInitialize()在单独的线程上崩溃而不返回
- c++中的线程池
- 线程之间的布尔停止信号
- 为什么std::async使用同一个线程运行函数
- 用于矢量处理的多个线程
- C++为线程工作动态地分割例程
- 为什么我不能在 while 循环中创建线程?