C:线程同步使用pthreads,加锁和解锁mutex
C: Thread synchronization using pthreads, locking and unlocking mutex
我正在从事一个更大的项目,其中包括两个线程,其中一个数据库在它们之间共享。一个线程的工作是对每个活动元素计时器进行倒计时,当该元素的计时器为零时,它应该将其设置为非活动状态并将其清零。
主线程只是以某种方式操纵活动元素的其他变量。
我不确定如何同步这些线程,尤其是当计时器线程在计时器达到零时清理数据库元素时。现在我认为在 main()
的 while(1)
循环中解锁和锁定 mutex
之间的时间可能太快了?
如果计时器线程正在等待mutex
在pthread_mutex_lock()
解锁;它在 main()
中解锁,我们是否知道 计时器线程 将是下一个锁定 mutex
的线程,或者 main()
可能是这样很快它再次锁定它,因为 pthread_mutex_unlock()
和 pthread_mutex_lock()
?
之间没有任务
我没有使用条件变量的经验,这是个好主意吗?
这是我更大项目的一个最小工作示例。
#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <time.h>
#define NUM_COMPUTER 10
typedef struct computer
{
unsigned int active;
unsigned int timer;
int x;
}computer_t;
/**
* Function declarations
*/
void activate(computer_t* comp);
void* timerFunction(void* args);
/**
* Global variables
*/
computer_t database[NUM_COMPUTER];
pthread_mutex_t mutex;
main() 函数
/**
* Main
*/
int main(void)
{
memset(database, 0, sizeof database);
// Initialize some database elements
activate(database);
database[0].x = 5;
activate(database + 3);
database[3].x = 23;
activate(database + 9);
database[9].x = -7;
pthread_t timer_thread;
pthread_create(&timer_thread, NULL, timerFunction, NULL);
while(1)
{
/**
* MAY PROBLEMS OCCUR HERE? UNLOCKING AND LOCKING ALMOST DIRECTLY
*/
// ************* LOCK *************
pthread_mutex_lock(&mutex);
/**
* Manipulate struct members
*/
for (int i = 0; i < NUM_COMPUTER; i++)
{
if (database[i].active)
{
database[i].x += 1;
}
}
// ************* UNLOCK *************
pthread_mutex_unlock(&mutex);
}
return 0;
}
额外功能
void activate(computer_t* comp)
{
comp->active = 1;
comp->timer = 100;
}
void* timerFunction(void* args)
{
struct timespec ts;
// Sleep interval before checking database again
ts.tv_sec = 1;
ts.tv_nsec = 0;
while(1)
{
// ************* LOCK *************
pthread_mutex_lock(&mutex);
/**
* Go through every database index
*/
for (int index = 0; index < NUM_COMPUTER; index++)
{
if (database[index].active)
{
if (database[index].timer > 0)
{
database[index].timer--;
}
else
{
/**
* Clean up database index
*/
memset(database + index, 0, sizeof database);
}
}
}
// ************* UNLOCK *************
pthread_mutex_unlock(&mutex);
/**
* Sleep 1 sec before checking database again
*/
nanosleep(&ts, NULL);
}
}
If the timer thread is waiting for the mutex
to unlock at
pthread_mutex_lock()
; and it unlocks in main()
, do we know that the
timer thread will be the next one to lock the mutex
没有
至少,一般情况下不会。这是线程调度策略的问题,您的特定平台可能允许也可能不允许您调整。 pthreads API 在这方面不作任何保证。
or might the
main()
be so fast that it locks it again as there is no task between
pthread_mutex_unlock()
and pthread_mutex_lock()
?
这不仅有可能,而且很有可能至少在某些时候发生。
I have no experience using condition variables, may that be a good
idea here?
条件变量是您随身携带的重要工具,但我认为没有理由认为它们在这里会特别有用。
您提供的代码的主要问题是,两个线程在活动时都必须锁定整个世界以供自己独占使用。因此,即使所有锁定都按预期工作,您也不会有真正的并发性,因此多线程会增加复杂性而不会带来任何实际优势。
如果您想继续使用该模式,那么我会放弃多线程,而是设置一个 POSIX timer 以 one-second 间隔(或您喜欢的任何间隔)升起一个标志。然后您的 single-threaded 程序可以在每次循环迭代的顶部检查该标志,以确定是执行其正常工作还是执行一次超时管理。
如果您想要真正的并发性,那么您可能需要缩小互斥体保护的关键区域,或者从保护所有内容的单个互斥体更改为多个互斥体,每个互斥体保护所有事物的一个子集。也许您可以替代地围绕原子对象和操作而不是互斥锁来重新设计,但到目前为止我所看到的并没有给我太多信心。
我正在从事一个更大的项目,其中包括两个线程,其中一个数据库在它们之间共享。一个线程的工作是对每个活动元素计时器进行倒计时,当该元素的计时器为零时,它应该将其设置为非活动状态并将其清零。
主线程只是以某种方式操纵活动元素的其他变量。
我不确定如何同步这些线程,尤其是当计时器线程在计时器达到零时清理数据库元素时。现在我认为在 main()
的 while(1)
循环中解锁和锁定 mutex
之间的时间可能太快了?
如果计时器线程正在等待mutex
在pthread_mutex_lock()
解锁;它在 main()
中解锁,我们是否知道 计时器线程 将是下一个锁定 mutex
的线程,或者 main()
可能是这样很快它再次锁定它,因为 pthread_mutex_unlock()
和 pthread_mutex_lock()
?
我没有使用条件变量的经验,这是个好主意吗?
这是我更大项目的一个最小工作示例。
#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <time.h>
#define NUM_COMPUTER 10
typedef struct computer
{
unsigned int active;
unsigned int timer;
int x;
}computer_t;
/**
* Function declarations
*/
void activate(computer_t* comp);
void* timerFunction(void* args);
/**
* Global variables
*/
computer_t database[NUM_COMPUTER];
pthread_mutex_t mutex;
main() 函数
/**
* Main
*/
int main(void)
{
memset(database, 0, sizeof database);
// Initialize some database elements
activate(database);
database[0].x = 5;
activate(database + 3);
database[3].x = 23;
activate(database + 9);
database[9].x = -7;
pthread_t timer_thread;
pthread_create(&timer_thread, NULL, timerFunction, NULL);
while(1)
{
/**
* MAY PROBLEMS OCCUR HERE? UNLOCKING AND LOCKING ALMOST DIRECTLY
*/
// ************* LOCK *************
pthread_mutex_lock(&mutex);
/**
* Manipulate struct members
*/
for (int i = 0; i < NUM_COMPUTER; i++)
{
if (database[i].active)
{
database[i].x += 1;
}
}
// ************* UNLOCK *************
pthread_mutex_unlock(&mutex);
}
return 0;
}
额外功能
void activate(computer_t* comp)
{
comp->active = 1;
comp->timer = 100;
}
void* timerFunction(void* args)
{
struct timespec ts;
// Sleep interval before checking database again
ts.tv_sec = 1;
ts.tv_nsec = 0;
while(1)
{
// ************* LOCK *************
pthread_mutex_lock(&mutex);
/**
* Go through every database index
*/
for (int index = 0; index < NUM_COMPUTER; index++)
{
if (database[index].active)
{
if (database[index].timer > 0)
{
database[index].timer--;
}
else
{
/**
* Clean up database index
*/
memset(database + index, 0, sizeof database);
}
}
}
// ************* UNLOCK *************
pthread_mutex_unlock(&mutex);
/**
* Sleep 1 sec before checking database again
*/
nanosleep(&ts, NULL);
}
}
If the timer thread is waiting for the
mutex
to unlock atpthread_mutex_lock()
; and it unlocks inmain()
, do we know that the timer thread will be the next one to lock themutex
没有
至少,一般情况下不会。这是线程调度策略的问题,您的特定平台可能允许也可能不允许您调整。 pthreads API 在这方面不作任何保证。
or might the
main()
be so fast that it locks it again as there is no task betweenpthread_mutex_unlock()
andpthread_mutex_lock()
?
这不仅有可能,而且很有可能至少在某些时候发生。
I have no experience using condition variables, may that be a good idea here?
条件变量是您随身携带的重要工具,但我认为没有理由认为它们在这里会特别有用。
您提供的代码的主要问题是,两个线程在活动时都必须锁定整个世界以供自己独占使用。因此,即使所有锁定都按预期工作,您也不会有真正的并发性,因此多线程会增加复杂性而不会带来任何实际优势。
如果您想继续使用该模式,那么我会放弃多线程,而是设置一个 POSIX timer 以 one-second 间隔(或您喜欢的任何间隔)升起一个标志。然后您的 single-threaded 程序可以在每次循环迭代的顶部检查该标志,以确定是执行其正常工作还是执行一次超时管理。
如果您想要真正的并发性,那么您可能需要缩小互斥体保护的关键区域,或者从保护所有内容的单个互斥体更改为多个互斥体,每个互斥体保护所有事物的一个子集。也许您可以替代地围绕原子对象和操作而不是互斥锁来重新设计,但到目前为止我所看到的并没有给我太多信心。