1. 一次性初始化:
互斥量智能初始化一次,再次初始化會出現錯誤。比如自己寫庫函數時將互斥量封裝進去,這時一次性初始化就用上場了。
定義變量pthread_once_t once_control = PTHREAD_ONCE_INIT;
void init_routine(){
//初始化互斥量
//初始化讀寫鎖
}
接下來在函數pthread_once引用上述變量和函數,多線程只會執行函數指針一次。
2. 線程屬性
創建線程時可以使用pthread_attr_t類型參數變更屬性。
如:
a. detachstate線程的分離狀態
b. guardsize線程棧末尾的警戒區域大小//預防棧溢出
c. stackaddr線程棧的最低地址
d. stacksize線程棧的大小//不能小於最小值
並不是全部線程都支持這些熟悉,修改前需要先檢查。
pthread_attr_setstack //修改棧地址、大小
pthread_attr_getstack //獲取棧屬性
pthread_attr_setstacksize //設置大小,不能設地址
pthread_attr_sgetstacksize
3. 線程的同步屬性
1). 互斥量的屬性:
a.進程共享屬性:PTHREAD_PROCESS_PRIVATE,同一進程的多個線程訪問權限;PTHREAD_PROCESS_SHARED:多個進程訪問權限。
如果互斥量在多進程的共享內存區域,那麼具有這個屬性的互斥量可以同步多進程。
pthread_mutexattr_getpshared/pthread_mutexattr_setshared設置、獲取互斥量進程共享屬性。
b.類型屬性:設置互斥量的類型屬性,PTHREAD_MUTEX_NORMAL等;
pthread_mutexattr_settype/pthread_mutexattr_gettype設置、查看出現
2).讀寫鎖屬性
3)條件變量屬性
4. 示例,互斥量屬性操作
#include "apue.h"
int main()
{
char *shm = "myshm";
char *shm1 = "myshm1";
int shm_id, shm_id1;
char *buf;
pid_t pid;
pthread_mutex_t *mutex;
pthread_mutexattr_t mutexattr;
//打開共享內存
shm_id1 = shm_open(shm1, O_RDWR|O_CREAT, 0644);
//調整共享內存大小
ftruncate(shm_id1, 100);
//映射共享內存,MAP_SHARED屬性表明,對共享內存的任何修改都會影響其他進程
mutex =(pthread_mutex_t *)mmap(NULL, 100, PROT_READ|PROT_WRITE, MAP_SHARED, shm_id1, 0);
pthread_mutexattr_init(&mutexattr);
#ifdef _POSIX_THREAD_PROCESS_SHARED
pthread_mutexattr_setpshared(&mutexattr, PTHREAD_PROCESS_SHARED);
#endif
pthread_mutex_init(mutex, &mutexattr);
//打開共享內存
shm_id = shm_open(shm, O_RDWR|O_CREAT, 0644);
//調整共享內存大小
ftruncate(shm_id, 100);
//映射共享內存,MAP_SHARED屬性表明,對共享內存的任何修改都會影響其他進程
buf =(char *)mmap(NULL, 100, PROT_READ|PROT_WRITE, MAP_SHARED, shm_id, 0);
pid = fork();
if(pid==0)
{
//休眠1s,讓父進程先運行
sleep(1);
printf("I'm child proccess\n");
pthread_mutex_lock(mutex);
//將共享內存內存修改爲hello
memcpy(buf, "hello", 6);
printf("child buf is : %s\n", buf);
pthread_mutex_unlock(mutex);
}
else if(pid>0)
{
printf("I'm parent proccess\n");
pthread_mutex_lock(mutex);
//修改共享內存到內容,改爲world
memcpy(buf, "world", 6);
sleep(3);
printf("parent buf is : %s\n", buf);
pthread_mutex_unlock(mutex);
}
pthread_mutexattr_destroy(&mutexattr);
pthread_mutex_destroy(mutex);
//解除映射
munmap(buf, 100);
//消除共享內存
shm_unlink(shm);
//解除映射
munmap(mutex, 100);
//消除共享內存
shm_unlink(shm1);
}
5. 線程私有數據
多個函數多個進程可以訪問這個變量,看起來像全局變量;但是它又不是全局變量,線程對這個變量的訪問不會彼此產生影響。比如參數errno。
使用私有數據前,要先創建一個與私有數據相關的key,類型pthread_key_t.
使用函數pthread_key_create創建鍵,創建的鍵放在參數key指向的內存單元,另一個參數destructor是析構函數。
pthread_key_delete銷燬鍵,當鍵銷燬後與它關聯的數據並沒有被銷燬!
pthread_setspecific將私有數據與key關聯
pthread_getspecific獲取私有數據的地址
6. 示例,使用私有數據
/*DATE: 2015-4-17
*AUTHOR: WJ
*DESCRIPTION: 線程到私有數據, 一個像errno一樣到數據
*/
#include "apue.h"
pthread_key_t key;
void *thread_fun1(void *arg)
{
printf("thread 1 start!\n");
int a = 1;
//將a和key關聯
pthread_setspecific(key, (void *)a);
sleep(2);
printf("thread 1 key->data is %d\n", pthread_getspecific(key));
}
void *thread_fun2(void *arg)
{
sleep(1);
printf("thread 2 start!\n");
int a = 2;
//將a和key關聯
pthread_setspecific(key, (void *)a);
printf("thread 2 key->data is %d\n", pthread_getspecific(key));
}
int main()
{
pthread_t tid1, tid2;
//創造一個key
pthread_key_create(&key, NULL);
//創造新線程
if(pthread_create(&tid1, NULL, thread_fun1, NULL))
{
printf("create new thread 1 failed\n");
return;
}
if(pthread_create(&tid2, NULL, thread_fun2, NULL))
{
printf("create new thread 2 failed\n");
return;
}
//等待新線程結束
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
pthread_key_delete(key);
return;
}
7. 線程與fork
當線程fork時,爲子進程創建了整個進程地址空間的副本,子進程通過繼承整個地址空間的副本,也會將父進程的互斥量、讀寫鎖、條件變量的狀態繼承過來。如果父進程中互斥量是鎖着的,那麼子進程中互斥量也是鎖着的,雖然子進程自己還沒lock。這時,子進程無法解鎖。
子進程內部只有一個線程,由父進程中調用fork函數的線程副本構成。如果調用fork的線程將互斥量鎖住,那麼子進程會拷貝一個pthread_mutex_lock副本。這時子進程就有機會解鎖。
pthread_atfork函數
參數prepare在fork調用前會被調用
參數parent在fork返回父進程前調用
參數child在fork返回子進程前調用
8. 示例,線程與fork安全
/*DATE: 2015-4-17
*AUTHOR: DDDDD
*DESCRIPTION: 安全的使用fork
* prepare 在調用fork之前會被調用
* parent 在fork返回父進程之前被調用
* child 在fork返回子進程之前被調用
*/
#include "apue.h"
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void prepare()
{
pthread_mutex_lock(&mutex);
printf("I'm prepare\n");
}
void parent()
{
pthread_mutex_unlock(&mutex);
printf("I'm parent\n");
}
void child()
{
pthread_mutex_unlock(&mutex);
printf("I'm child\n");
}
void *thread_fun(void *arg)
{
sleep(1);
pid_t pid;
pthread_atfork(prepare, parent, child);
pid = fork();
if(pid==0)
{
pthread_mutex_lock(&mutex);
printf("child process\n");
pthread_mutex_unlock(&mutex);
}
if(pid>0)
{
pthread_mutex_lock(&mutex);
printf("parent process\n");
pthread_mutex_unlock(&mutex);
}
}
int main()
{
pthread_t tid;
if(pthread_create(&tid, NULL, thread_fun, NULL))
{
printf("create new thread failed\n");
return;
}
pthread_mutex_lock(&mutex);
sleep(2);
printf("main\n");
pthread_mutex_unlock(&mutex);
pthread_join(tid, NULL);
pthread_mutex_destroy(&mutex);
return;
}
注意:線程安全是gcc -pthread 編譯
線程非安全是gcc-lpthread編譯