一、线程的概念
线程是指进程内部的一个执行单元,也称为轻量级进程,它和进程一样可以独立执行、拥有独立的栈空间、程序计数器和寄存器等,但是多个线程可以共享进程的资源,如内存空间、文件、I/O。线程的创建和销毁都比进程更加的轻便快捷,因此多线程成为了提高系统性能和程序效率的一种重要方式。
二、线程的创建
Linux提供了几种方式创建线程:pthread_create()
、clone()
等。其中pthread_create()
是比较常用的一个函数,其函数原型如下:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
它的参数依次为:
- 指向线程标识符的指针;
- 线程属性,
NULL
表示使用默认属性; - 指向函数的指针,该函数是新线程的入口地址;
- 函数的参数。 下面是一个简单的例子:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
void *print_message_function(void *ptr);
int main()
{
pthread_t thread1, thread2;
char *message1 = "Thread 1";
char *message2 = "Thread 2";
int ret1, ret2;
// 创建线程1
ret1 = pthread_create(&thread1, NULL, print_message_function, (void *) message1);
if (ret1 != 0)
{
printf("Create thread 1 failed!\n");
exit(1);
}
// 创建线程2
ret2 = pthread_create(&thread2, NULL, print_message_function, (void *) message2);
if (ret2 != 0)
{
printf("Create thread 2 failed!\n");
exit(1);
}
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
printf("Main function exits!\n");
return 0;
}
void *print_message_function(void *ptr)
{
char *message = (char *) ptr;
printf("%s\n", message);
}
在上面的例子中,我们使用pthread_create()
函数创建了2个线程,并传入一个字符串作为线程函数的参数。然后使用pthread_join()
函数等待线程结束。
三、线程的同步
线程之间的同步通常需要互斥量、条件变量等机制。比如常用的互斥量pthread_mutex_t
可以用来保护共享资源,pthread_cond_t
则可以用来进行线程的条件等待与唤醒。
下面是一个使用互斥量保护共享资源的例子:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#define THREAD_NUM 5
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int sum = 0;
void *thread_function(void *arg);
int main()
{
pthread_t threads[THREAD_NUM];
// 创建多个线程
for (int i = 0; i < THREAD_NUM; i++)
{
pthread_create(&threads[i], NULL, thread_function, NULL);
}
// 等待所有线程结束
for (int i = 0; i < THREAD_NUM; i++)
{
pthread_join(threads[i], NULL);
}
printf("Sum = %d\n", sum);
return 0;
}
void *thread_function(void *arg)
{
int i;
for (i = 0; i < 1000; i++)
{
pthread_mutex_lock(&mutex);
sum++;
pthread_mutex_unlock(&mutex);
}
pthread_exit(NULL);
}
在这个例子中,我们使用pthread_mutex_t
来保护sum
这个共享资源,多个线程会去累加这个值。由于多个线程同时访问sum
,如果没有互斥保护,会产生竞争状态,导致结果不正确。因此我们使用互斥量对共享资源进行保护,保证线程之间的同步。
四、线程的销毁
线程的销毁通常是在线程函数中通过pthread_exit()
函数进行,也可以使用pthread_cancel()
函数强制取消线程。同时可以使用pthread_join()
函数等待线程结束,防止进程过早退出,导致线程没有执行完毕。
下面是一个使用pthread_cancel()
函数强制取消线程的例子:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
void *thread_function(void *arg);
int main()
{
pthread_t thread_id;
int ret;
// 创建线程
ret = pthread_create(&thread_id, NULL, thread_function, NULL);
if (ret != 0)
{
printf("Create thread failed!\n");
exit(1);
}
// 睡眠 5 秒
sleep(5);
// 强制杀死线程
pthread_cancel(thread_id);
printf("Main function exits!\n");
return 0;
}
void *thread_function(void *arg)
{
int i;
// 设置取消状态
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
// 设置取消类型
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
for (i = 0; ; i++)
{
printf("Thread running...\n");
sleep(1);
}
pthread_exit(NULL);
}
在上面的例子中,我们使用了pthread_cancel()
函数强制取消线程。同时在线程函数中设置了取消状态和取消类型。
五、线程的属性
线程的属性可以用来调整线程的优先级、栈大小等。pthread_attr_t
是线程属性的类型,Linux提供了一些函数来进行线程属性的设置和获取,如pthread_attr_init()
、pthread_attr_setstacksize()
、pthread_attr_getschedpolicy()
等。
下面是一个设置线程栈大小的例子:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
void *thread_function(void *arg);
int main()
{
pthread_t thread_id;
pthread_attr_t attr;
size_t stacksize;
int ret;
// 初始化线程属性
pthread_attr_init(&attr);
pthread_attr_getstacksize(&attr, &stacksize);
printf("Default stack size = %d KB\n", (int) stacksize / 1024);
// 设置线程栈大小
pthread_attr_setstacksize(&attr, 1024 * 1024);
pthread_attr_getstacksize(&attr, &stacksize);
printf("New stack size = %d KB\n", (int) stacksize / 1024);
// 创建线程
ret = pthread_create(&thread_id, &attr, thread_function, NULL);
if (ret != 0)
{
printf("Create thread failed!\n");
exit(1);
}
// 等待线程结束
pthread_join(thread_id, NULL);
printf("Main function exits!\n");
return 0;
}
void *thread_function(void *arg)
{
int i;
for (i = 0; i < 10; i++)
{
printf("Thread running...\n");
sleep(1);
}
pthread_exit(NULL);
}
在上面的例子中,我们使用了pthread_attr_setstacksize()
函数设置了线程的栈大小为1MB。
六、总结
本文介绍了Linux下创建线程的方法及相关知识。具体来说,我们讲解了线程的概念、创建、同步、销毁和属性等内容。通过本文的学习,读者可以掌握如何使用Linux提供的相关函数来创建线程,并进行进一步的开发。