lesson5 線程的高級屬性

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編譯

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章