Linux下C语言编程之线程学习

 

进程和线程的区别

进程是资源分配的最小单位,线程是程序执行的最小单位。

进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段,这种操作非常昂贵。而线程是共享进程中的数据的,使用相同的地址空间,因此CPU切换一个线程的花费远比进程要小很多,同时创建一个线程的开销也比进程要小很多。

线程之间的通信更方便,同一进程下的线程共享全局变量、静态变量等数据,而进程之间的通信需要以通信的方式(IPC)进行。不过如何处理好同步与互斥是编写多线程程序的难点。

但是多进程程序更健壮,多线程程序只要有一个线程死掉,整个进程也死掉了,而一个进程死掉并不会对另外一个进程造成影响,因为进程有自己独立的地址空间。

创建线程

创建线程

原型

#include <pthread.h>

int pthread_create(pthread_t *tid, pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);

功能

创建一个线程

新线程从start_routine开始执行

新线程的ID保存在tid指向的位置

参数

参数功能
tid该参数是一个指针, 新线程的ID保存在tid指向的位置
attr线程属性。如果为空,则使用缺省的属性值
start_routine该参数是一个函数指针, 新线程从start_routine开始执行
arg提供给start_routine的参数

返回值

如果成功,返回0

如果失败,返回非0

线程之间也是并发执行的,举例:

#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<unistd.h>
#include<pthread.h>

void *compute()
{
	char i;
	for(i = 'a';i < 'd';i++)
	{
		printf("%c\n",i);
		sleep(1);
	}
}
int main( int argc , char *argv[] )  
{
	pthread_t id;
	pthread_create(&id,NULL,&compute,NULL);
	char i;
	for(i = 'e';i <= 'g';i++)
	{
		printf("%c\n",i);
		sleep(1);
	}
}

结果显示:

e
a
f
b
g
c

可以看出是并发执行的。

等待线程

原型

#include <pthread.h>

int pthread_join(pthread_t tid, void **result);

功能

等待线程结束

参数

tid目标线程的ID
result用于存放线程的计算结果

返回值

如果成功,返回0

如果失败,返回非0

void *start_routine(void *arg)
{
    void *result;
    ...
    return result;
}

举例:

#include<stdio.h>
#include<pthread.h>

int result = 0;
void *compute()
{
	int i = 0;
	for( i = 4;i <= 6;i++)
		result += i;
}

void main_compute()
{
	int i = 0;
	for(i = 1;i <= 3;i++)
		result += i;
}
int main( int argc , char *argv[] )  
{
	pthread_t id;
	pthread_create(&id,NULL,compute,NULL);
	main_compute();
	//pthread_join(id,NULL);
	printf("%d\n",result);
	return 0;
}

执行结果是:

[wanghe@localhost ~]$ ./test.exe 
6

 由于主线程执行时间太短,子线程还没开始执行就结束了。把注释代码去掉注释,主线程会等子线程结束,结果如下:

[wanghe@localhost ~]$ ./test.exe 
21

线程返回值

线程入口函数返回类型为void **类型的结果

等待线程函数pthread_join获取线程的返回结果

void *result;
pthread_join(tid, &result);

举例:

#include<stdio.h>
#include<pthread.h>
#include<malloc.h>

void *compute()
{
    int *result;
    result = (int*)malloc(sizeof(int));
    *result = 4;
    return result;
}
int main()
{
    pthread_t id;
    int *result;
    pthread_create(&id,NULL,compute,NULL);
    pthread_join(id,(void **)&result);
    printf("%d\n",*result);
    return 0;
}

 运行结果如下,注意代码中指针一开始只是一个变量,如果需要返回值记得malloc开辟空间!

[wanghe@localhost ~]$ ./pthread.exe 
4

线程互斥

初始化互斥量

原型

#include <pthread.h>

int pthread_mutex_init(pthread_mutex_t *mutex, pthread_mutexattr_t *attr);
int pthread_mutex_destroy(pthread_mutex_t *mutex);

功能

pthread_mutex_init初始化互斥量

pthread_mutex_destroy释放互斥量

参数

如果attr等于NULL,则使用缺省的属性进行初始化

返回值

如果成功,返回0

如果失败,返回非0

 

加锁解锁

原型

#include <pthread.h>

int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);

功能

pthread_mutex_lock将互斥量加锁

pthread_mutex_unlock将互斥量解锁

返回值

如果成功,返回0

如果失败,返回非0

举例:

#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include<string.h>

pthread_mutex_t mutex;
void *print(void *s)
{
    char *str = (char *)s;
  //  pthread_mutex_lock(&mutex);
    int i = 0;
    for(i = 0;i < strlen(str);i++)
    {
        printf("%c\n",str[i]);
        usleep(10);
    }
  //  pthread_mutex_unlock(&mutex);
}

int main()
{
    pthread_t id[2];
    char *str1 = "hello";
    char *str2 = "world";
    //pthread_mutex_init(&mutex,NULL);
    pthread_create(&id[0],NULL,&print,(void *)str1);
    pthread_create(&id[1],NULL,&print,(void *)str2);

    pthread_join(id[0],NULL);
    pthread_join(id[1],NULL);
//    pthread_mutex_destroy(&mutex);
    return 0;
}

运行结果:

[wanghe@localhost ~]$ ./test_mutex.exe 
h
w
o
e
r
l
l
l
o
d

 可以看出输出是乱序的,明显不符合我们的需求,把注释去掉运行:

[wanghe@localhost ~]$ ./test_mutex.exe 
w
o
r
l
d
h
e
l
l
o
[wanghe@localhost ~]$ ./test_mutex.exe 
h
e
l
l
o
w
o
r
l
d

虽然单词顺序不一定一致,但单词是完整的,由此可见互斥量的重要性!

条件变量

初始化

原型:

#include <pthread.h>

int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *attr);
int pthread_cond_destroy(pthread_cond_t *cond);

功能

pthread_cond_init初始化条件变量

pthread_cond_destroy释放条件变量

参数

如果attr等于NULL,则使用缺省的属性进行初始化

返回值

如果成功,返回0

如果失败,返回非0

等待

原型:

#include <pthread.h>

int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);

功能

阻塞当前线程的运行

参数

cond

当前线程在条件变量上阻塞

mutex

当前线程阻塞时所在的临界区

返回值

如果成功,返回0

如果失败,返回非0

 

唤醒线程

原型:

#include <pthread.h>

int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);

功能

pthread_cond_signal唤醒阻塞在条件变量上的一个线程

pthread_cond_broadcast唤醒阻塞在条件变量上的所有线程

返回值

如果成功,返回0

如果失败,返回非0

 

为什么要引入条件变量呢?

假设我们现在需要做一个生产者消费者模型,生产者对带有头节点的链表头插方式push_front生产数据,消费者调用pop_front消费数据.而生产者可能动作比较慢,这时就会有问题。

生产者生产一个数据时间,消费者可能迫切需求。因此,一直轮寻申请锁资源,以便进行消费。所以就会产生多次不必的锁资源申请释放动作。影响系统性能。那么,解决这一问题的方案就是:条件变量。

根据是否符合条件来决定线程是否阻塞,满足条件则执行,不满足条件则阻塞,就可以提升系统性能。

注:一个Condition Variable总是和一个Mutex搭配使用的。一个线程可以调用pthread_cond_wait 在一一个Condition Variable上阻塞等待,这个函数做以下三步操作:

1. 释放Mutex

2. 阻塞等待

3. 当被唤醒时,重新获得Mutex并返回

信号量机制完成生产者消费者:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

#define CAPACITY 4
int buffer[CAPACITY];
int in;
int out;

int buffer_is_empty()
{
    return in == out;
}

int buffer_is_full()
{
    return (in + 1) % CAPACITY == out;
}

int get_item()
{
    int item;

    item = buffer[out];
    out = (out + 1) % CAPACITY;
    return item;
}

void put_item(int item)
{
    buffer[in] = item;
    in = (in + 1) % CAPACITY;
}

typedef struct {
    int value;
    pthread_mutex_t mutex;
    pthread_cond_t cond;
} sema_t;

void sema_init(sema_t *sema, int value)
{
    sema->value = value;
    pthread_mutex_init(&sema->mutex, NULL);
    pthread_cond_init(&sema->cond, NULL);
}

void sema_wait(sema_t *sema)
{
    pthread_mutex_lock(&sema->mutex);
    while (sema->value <= 0)
        pthread_cond_wait(&sema->cond, &sema->mutex);
    sema->value--;
    pthread_mutex_unlock(&sema->mutex);
}

void sema_signal(sema_t *sema)
{
    pthread_mutex_lock(&sema->mutex);
    ++sema->value;
    pthread_cond_signal(&sema->cond);
    pthread_mutex_unlock(&sema->mutex);
}

sema_t mutex_sema;
sema_t empty_buffer_sema;
sema_t full_buffer_sema;

#define ITEM_COUNT (CAPACITY * 2)

void *consume(void *arg)
{
    int i;
    int item;

    for (i = 0; i < ITEM_COUNT; i++) { 
        sema_wait(&full_buffer_sema);
        sema_wait(&mutex_sema);

        item = get_item();
        printf("    consume item: %c\n", item); 

        sema_signal(&mutex_sema);
        sema_signal(&empty_buffer_sema);
    }

    return NULL;
}

void *produce()
{
    int i;
    int item;

    for (i = 0; i < ITEM_COUNT; i++) { 
        sema_wait(&empty_buffer_sema);
        sema_wait(&mutex_sema);

        item = i + 'a';
        put_item(item);
        printf("produce item: %c\n", item); 

        sema_signal(&mutex_sema);
        sema_signal(&full_buffer_sema);
    }

    return NULL;
}

int main()
{ 
    pthread_t consumer_tid;

    sema_init(&mutex_sema, 1);
    sema_init(&empty_buffer_sema, CAPACITY - 1);
    sema_init(&full_buffer_sema, 0);

    pthread_create(&consumer_tid, NULL, consume, NULL);
    produce();
    pthread_join(consumer_tid, NULL);
    return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值