我正在为学校做作业,其中一个要求是我不能使用全局变量,但我确实需要共享内存的静态变量。赋值的前提是使用pthread库和信号量保证创建的线程逆序执行。我已经让它与全局静态信号量/condvar/mutex 一起工作:
#include <pthread.h>
#include <stdio.h>
#include <iostream>
#include <semaphore.h>
using namespace std;
#define NUM 5
static sem_t threadCounter;
static pthread_cond_t nextThreadCond = PTHREAD_COND_INITIALIZER;
static pthread_cond_t makingThreadCond = PTHREAD_COND_INITIALIZER;
static pthread_mutex_t makingThreadMutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t nextThreadMutex = PTHREAD_MUTEX_INITIALIZER;
void *wait_func(void *args)
{
// cout<<"Waiting"<<endl;
// pthread_cond_wait(&makingThreadCond, &makingThreadMutex);
// cout<<"Woke up"<<endl;
int tid = *((int *)args);
int val;
sem_getvalue(&threadCounter, &val);
// cout << tid << ":" << val << endl;
while (tid != val-1)
{
pthread_cond_wait(&nextThreadCond, &nextThreadMutex);
sem_getvalue(&threadCounter, &val);
// cout<<"evaluating condition in"<<tid<<", val is "<<val<<endl;
}
sem_wait(&threadCounter); // decrement threadCounter
// cout << "after decrement" << endl;
sem_getvalue(&threadCounter, &val);
// cout << "decremented val "<<val << endl;
cout<<"Exiting thread #"<<tid<<endl;
pthread_mutex_unlock(&nextThreadMutex);
// cout<<"after nextThreadMutex unlock"<<endl;
pthread_cond_broadcast(&nextThreadCond);
// cout<<"after nextThreadCond broadcast"<<endl;
}
int main()
{
pthread_t tid[NUM];
if (sem_init(&threadCounter, 0, NUM) < 0)
{
cout << "Failed to init sem" << endl;
}
for (int i = 0; i < NUM; i++)
{
int *argId = (int *)malloc(sizeof(*argId));
*argId = i;
if (pthread_create(&tid[i], NULL, wait_func, argId))
{
cout << "Couldn't make thread " << i << endl;
}
}
for (int i = 0; i < NUM; i++)
{
pthread_join(tid[i], NULL);
}
}
但正如我所说,这是不允许的,所以我尝试将其转换为通过结构共享它们的位置,并使用 pthread_create 参数传入:
#include <pthread.h>
#include <stdio.h>
#include <iostream>
#include <semaphore.h>
using namespace std;
#define NUM 5
struct args
{
int tid;
sem_t* sem;
pthread_cond_t* cond;
pthread_mutex_t* mut;
};
void *wait_func(void *args_ptr)
{
// cout<<"Waiting"<<endl;
// pthread_cond_wait(&makingThreadCond, &makingThreadMutex);
// cout<<"Woke up"<<endl;
struct args* args = (struct args*) args_ptr;
int tid = (args->tid);
pthread_cond_t cond = *(args->cond);
pthread_mutex_t mut = *(args->mut);
sem_t sem = *(args->sem);
int val;
sem_getvalue(&sem, &val);
// cout << tid << ":" << val << endl;
while (tid != val - 1)
{
pthread_cond_wait(&cond, &mut);
sem_getvalue(&sem, &val);
// cout<<"evaluating condition in"<<tid<<", val is "<<val<<endl;
}
sem_wait(&sem); // decrement threadCounter
// cout << "after decrement" << endl;
sem_getvalue(&sem, &val);
// cout << "decremented val "<<val << endl;
cout << "Exiting thread #" << tid << endl;
pthread_mutex_unlock(&mut);
// cout<<"after nextThreadMutex unlock"<<endl;
pthread_cond_broadcast(&cond);
// cout<<"after nextThreadCond broadcast"<<endl;
}
int main()
{
static sem_t threadCounter;
static pthread_cond_t nextThreadCond = PTHREAD_COND_INITIALIZER;
static pthread_mutex_t nextThreadMutex = PTHREAD_MUTEX_INITIALIZER;
pthread_t tid[NUM];
if (sem_init(&threadCounter, 0, NUM) < 0)
{
cout << "Failed to init sem" << endl;
}
for (int i = 0; i < NUM; i++)
{
int *argId = (int *)malloc(sizeof(*argId));
*argId = i;
struct args args;
args.tid = *argId;
args.sem = &threadCounter;
args.cond = &nextThreadCond;
args.mut = &nextThreadMutex;
if (pthread_create(&tid[i], NULL, wait_func, &args))
{
cout << "Couldn't make thread " << i << endl;
}
}
// cout << "Before posting sem" << endl;
// sem_post(&makingThreads);
// cout << "Sem posetd" << endl;
// cout<<"Broadcasting"<<endl;
// pthread_cond_broadcast(&makingThreadCond);
for (int i = 0; i < NUM; i++)
{
pthread_join(tid[i], NULL);
}
}
这会立即卡住两次“Exiting thread #4”。我认为第二个代码等同于第一个代码,只是没有全局变量,但一定有我遗漏的东西。
最佳答案
struct args args;
这在 for
循环的范围内声明了一个对象。当执行到达 for
循环的末尾时,这个对象被销毁——就像在函数内或某个内部范围内本地声明的任何其他对象一样——这发生在循环再次从开始,或者如果 for
循环完全停止迭代。无论哪种方式,一旦执行到下一个 }
这个对象就会消失。它永远消失了。它被摧毁了。它已经不复存在了。它加入了无形的唱诗类。它成为一个前对象。
但在此之前,在此循环结束之前,会发生以下情况:
if (pthread_create(&tid[i], NULL, wait_func, &args))
所以你启动了一个新的执行线程,并向它传递了一个指向这个对象的指针,这个对象即将遇到它的制造者。
一旦 pthread_create()
返回,循环就结束了,您的 args
对象就消失了,上面提到的情况发生了:它被销毁了;它已经不存在了;它加入了无形的合唱团;它变成了一个前对象。
并且 C 和 C++ 标准绝对不能保证您的新执行线程实际开始运行,并到达它读取此指针的位置,以及它指向的内容,在此循环结束之前.
而且,更有可能的是,每个新的执行线程都不会在主执行线程中读取指向 args
对象的指针,直到它被销毁很久之后。所以它从指向已销毁对象的指针中获取内容。再见。
因此,此执行线程的操作成为未定义的行为。
这解释了您观察到的随机、不可预测的行为。
通常的方法是将 malloc
或 new
everything 传递给新的执行线程,并且将指向 new
ed 或 malloc
ed 对象的指针传递给执行线程。
也可以仔细编写一些代码,让主执行线程停止并等待,直到新的执行线程检索到它需要做的事情,然后自己继续执行。如果您愿意,将需要更多代码来实现该方法。
您的代码也有您最初尝试采用这种方法的证据:
int *argId = (int *)malloc(sizeof(*argId));
*argId = i;
struct args args;
args.tid = *argId;
malloc
对该指针进行赋值,然后将其复制到 args.tid
完全没有任何用处。同样的事情可以简单地通过以下方式完成:
struct args args;
args.tid = i;
malloc
唯一做的就是泄漏内存。此外,由于上述原因,在 for
循环的内部范围内声明为局部变量的整个 args
对象注定要失败。
附言当采用“malloc the entire args
object”方法时,这也会泄漏内存,除非您也采取措施努力 free
malloc
ed 对象,在适当的时候这样做。
关于c++ - 从全局静态变量切换到静态变量会破坏代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59121679/