繁体   English   中英

使用带堆栈的线程(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 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM