
并非所有程序都必须使用多线程,有时多线程的性能不如单线程. 使用多线程的好处如下:
(1)多线程使用相同的地址空间并共享大多数数据. 与多进程相比,成本更为节省,并且必须将启动一个新进程分配给其独立的地址空间,这需要一个数据表. 维护代码段,数据段,堆栈段等.
(2)对于不同的进程,它们具有独立的数据空间,并且只能通过通信进行数据传输,这既费时又不方便. 数据可以在多个线程之间直接共享,例如共享全局变量. 共享全局变量时,应注意变量的同步性,否则容易造成灾难性后果.
(3)在有多个CPU的情况下,不同的线程可以在不同的CPU下运行,因此它们是完全并行的.
在这种情况下,多线程是理想的. 例如,分两个步骤执行任务,以提高工作效率,可以使用多线程技术打开两个线程,第一个线程是第一步,第二个线程是第二步. 这时,要注意同步. 因为只有第一步才能完成做第二步的工作. 此时,您可以使用同步技术程之间进行通信.
根据这种情况,说明多线程之间的通信. 在Windows平台下,用于多线程之间通信的主要方法是:
(1)共享全局变量,例如上面的问题,第一步是将收据传递给第二步,可以共享全局变量,让两个线程传递数据,这时的主要考虑是变量的同步,因为当后续线程对数据进行操作时,第一个线程会更改数据的内容,并且同步没有受到保护,因此后果非常严重(即读回脏数据). 在这种情况下,考虑同步的简单方法是设置bool标志. 例如,在第二个线程用完数据之前,第一个线程无法写入. 有时,当两个线程所需的时间不相同时,如何实现最大效率的同步会更加麻烦. 此时,可以再打开几个缓冲区进行操作. 如果两个线程已在运行,则由于时间不一致,缓冲区迟早会溢出. 在这种情况下,请考虑是允许写入数据还是覆盖旧数据. 此时,应分析具体问题. 也就是说,使用bool变量控制同步,Linux和Windows相同.
类似于上面的问题,共享全局变量同步问题. 除了使用布尔变量外,还有互斥体. 那是锁定的. 在Windows下锁定类似于在Linux下锁定. 互斥锁用于同步. 要输入该代码,必须首先获取互斥体.
在Windows下的互斥锁的功能是: createmutex()创建一个互斥锁,然后获取互斥锁waitforsingleobject()函数,并在其用尽时释放互斥锁ReleaseMutex(hMutex),当其减小为0时,内核仅释放其对象. 以下是Windows下相互排斥的几个函数原型.
HANDLE WINAPI CreateMutex(
__ in LPSECURITY_ATTRIBUTES lpMutexAttributes
__在BOOL bInitialOwner中,
__在LPCTSTR lpName中
);
可用于创建已命名或未命名的互斥对象
第一个参数可以指向结构SECURITY_ATTRIBUTES通常可以设置为null;
第二个参数是指当时的功能对于当前所有者而言是否处于归纳状态FALSE不会创建互斥
第三个参数指示它是否是众所周知的互斥对象. 如果未命名,则使用null.
DWORD WINAPI WaitForSingleObject(
__在HANDLE hHandle中,
__ in DWORD dwMilliseconds
);
第一个是创建的互斥锁的句柄. 第二个是将其设置为INFINITE宏后将返回的时间,直到用户定义返回值,它才会返回.
对于linux操作系统,互斥是相似的,只是功能不同. 在Linux下,与互斥有关的几个功能:
pthread_mutex_init函数: 初始化互斥锁;
pthread_mutex_destroy函数: 取消互斥锁;
pthread_mutex_lock函数: 锁定,如果失败,则阻塞并等待;
pthread_mutex_unlock函数: 解锁;
pthread_mutex_trylock函数: 测试锁定,如果失败,则立即返回,错误代码为EBUSY;
关于这些功能的用法,我在这里不会多谈. Windows下还有一种方法可以用来保护数据,它也是线程同步的方法
这是关键部分. 关键部分类似于互斥. 它们之间的区别是关键部分很快,但是它只能用于同步同一进程中的多个线程. 关键区域的获取和释放功能如下:
EnterCriticalSection()进入关键部分; LeaveCriticalSection()离开关键部分.
(2)消息机制用于多线程通信和同步. Windows下的消息机制功能主要用于后消息.
(3)Windows下的另一种线程通信方法是事件和信号量. 同样对于开头给出的示例,两个线程是同步的,并且信息在它们之间传递. 可以使用事件或信号量. 例如,第一个线程完成生产数据后,必须告诉第二个线程,他已经准备好数据,您可以将其拿走. 第二个线程获取数据. 在这里您可以使用消息机制. 当第一个线程准备好数据时,您可以直接将消息发送到第二个线程. 使用postmessage一个线程来解决此问题是合乎逻辑的.
对于linux,有一种类似的方法,即条件变量,其中Windows和linux不同.
对于Windows,使用事件和信号同步时,将使用waitforsingleobject()进行等待. 该函数的第一个参数是一个句柄,它可以是事件句柄或信号量句柄. 第二个参数是waiting延迟,到底要等待多长时间,单位为ms,如果此参数为INFINITE,则它无限期地等待. 释放信号量的函数是ReleaseSemaphore();释放事件的函数是SetEvent. 使用这些东西进行初始化.
对于linux操作系统,条件变量用于实现类似的功能. Linux的条件变量通常与互斥锁一起使用. 主要功能是:
pthread_mutex_lock
pthread_mutex_unlock,
pthread_cond_init

pthread_cond_signal
pthread_cond_wait
pthread_cond_timewait
要与Windows操作系统进行比较,请使用下表进行比较:

与上表比较,总结如下:
(1)Pthread_cleanup_push,Pthread_cleanup_pop:
这对函数push和pop的作用是在异常退出发生时执行一些清除操作,也就是说,当push和pop函数之间的异常退出(包括调用pthread_exit退出)时,push中的清除函数将如果有多次推入,则执行该命令,注意堆栈,先执行后一个函数,然后执行前一个函数,但请注意,当通过返回这两个函数之间的命令退出时,未执行推入的函数将查看弹出功能该参数不再为0. 另外,在没有异常退出的情况下,与返回退出的情况相同,即当弹出功能参数不为0时,执行清除操作,当弹出功能参数为0时,将执行清除功能. 推送功能未执行.
(2)pthread_cond_signal与Linux的SetEvent之间的区别
Pthread_cond_singal释放信号后,如果没有Pthread_cond_wait,则立即重置信号. 这与SetEvent不同,并且SetEvent不会重置. 详细说明如下:
有2种常见的模型用于设置和重置条件变量: 第一个模型是设置条件变量(带信号)时linux用不了多线程,如果当前没有线程正在等待,则其状态将保持设置(带信号),而状态不会更改为无信号,直到触发等待线程条目. 该模型由Windows平台上的自动设置事件表示.
第二个模型是pthread在Linux平台上使用的模型. 条件变量设置(带信号)后,即使当前没有线程在等待,其状态也将返回无符号状态.
在Linux平台上这种条件变量模型很难说. 在实际应用中,可以略微改进代码以避免这种差异. 由于这种差异仅在触发器不等待线程的条件变量时发生,因此仅需掌握触发器的时间即可. 最简单的方法是增加一个计数器,以记录等待线程的数量,并在决定触发条件变量之前检查该变量.
示例
使用pthread_cond_wait()和pthread_cond_signal()
pthread_mutex_t count_lock;
pthread_cond_t count_nonzero;
无符号数;
decrement_count()
{
pthread_mutex_lock(&count_lock);
同时(计数== 0)
pthread_cond_wait(&count_nonzero,&count_lock);
count = count-1;
pthread_mutex_unlock(&count_lock);
}
increment_count()
{
pthread_mutex_lock(&count_lock);
如果(计数== 0)
pthread_cond_signal(&count_nonzero);
count = count + 1;
pthread_mutex_unlock(&count_lock);
}
(3)当Pthread_cond_wait条件返回时,请注意互斥锁的解锁
extern int pthread_cond_wait __P((pthread_cond_t * __ cond,pthread_mutex_t * __ mutex));
调用此函数时,线程将解锁互斥锁所指向的锁,并被条件变量cond阻止. 可以通过函数pthread_cond_signal和函数pthread_cond_broadcast唤醒线程. 唤醒后,它将重新检查以查看是否满足条件. 如果不满意,通常该线程仍应在此处被阻塞,等待下一次唤醒. 如果使用pthread_cond_wait等待多线程处理,则互斥量将首先释放. 当等待信号到达时,将再次获得互斥锁,因此之后必须注意手动解锁. 示例如下:
#include
#include
#include
pthread_mutex_t互斥锁= PTHREAD_MUTEX_INITIALIZER; / *初始化互斥锁* /

pthread_cond_t cond = PTHREAD_COND_INITIALIZER; //初始化条件变量
void * thread1(void *);
void * thread2(void *);
int i = 1;
int main(void)
{
pthread_t t_a;
pthread_t t_b;
pthread_create(&t_a,NULL,thread1,(void *)NULL); / *创建进程t_a * /
pthread_create(&t_b,NULL,thread2,(void *)NULL); / *创建进程t_b * /
pthread_join(t_b,NULL); / *等待进程t_b结束* /
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
exit(0);
}
无效* thread1(无效*垃圾)
{
for(i = 1; i <= 9; i ++)
{
printf(“ IN one \ n”);
pthread_mutex_lock(&mutex); //
if(i%3 == 0)
pthread_cond_signal(&cond); / *,发送信号通知t_b进程* /
其他
printf(“ thead1: %d \ n”,i);
pthread_mutex_unlock(&mutex); // *解锁互斥锁* /
printf(“ Up Mutex \ n”);
sleep(3);
}
}
无效* thread2(无效*垃圾)
{
while(i <9)
{
printf(“在两个\ n”);
pthread_mutex_lock(&mutex);
if(i%3!= 0)
pthread_cond_wait(&cond,&mutex); / *等待* /
printf(“ thread2: %d \ n”,i);
pthread_mutex_unlock(&mutex);
printf(“ Down Mutex \ n”);

sleep(3);
}
}
输出如下:
合一
thead1: 1
互斥量
一分为二
合一
thead1: 2
互斥量
合一
thread2: 3
互斥体下
互斥量
合一
thead1: 4
互斥量
一分为二
合一
thead1: 5
互斥量
合一
互斥量
thread2: 6
互斥体下
一分为二
thread2: 6
互斥体下
合一
thead1: 7
互斥量
合一
thead1: 8
互斥量
一分为二
合一
互斥量
thread2: 9
互斥体下
请注意,蓝色有2个线程2: 6. 实际上,当该程序执行多次时,i = 3和i = 6可能会打印更多内容. 这是由于竞争锁定造成的.

(4)要注意的另一件事是Pthread_cond_timedwait等待绝对时间. 这与WaitForSingleObject不同. 互联网上也讨论了Pthread_cond_timedwait. 如下:
thread_a:
pthread_mutex_lock(&mutex);
//做点事情
pthread_mutex_unlock(&mutex)
thread_b:
pthread_mutex_lock(&mutex);
//做点事情
pthread_cond_timedwait(&cond,&mutex,&tm);
pthread_mutex_unlock(&mutex)
上面有两个线程thread_a,thread_b. 现在,如果a已进入临界区并且b同时超时,那么b是否将从pthread_cond_timedwait返回?如果可以返回,不是a和b都在关键部分吗?如果您无法返回,pthread_cond_timedwait的时间安排是否错误?
每个人都讨论了以下两个有价值的观点:
(1)pthread_cond_timedwait(pthread_cond_t * cv,pthread_mutex_t * external_mutex,const struct timespec * abstime)-此函数是pthread_cond_wait的基于时间的变体. 它会等待最长时间以通知cv. 如果在通知cv之前已经过了abstime,则该函数将以ETIME结果返回给调用者,表明发生了超时. 即使在超时的情况下,当pthread_cond_timedwait返回时,external_mutex也将被锁定.
(2)2.1 pthread_cond_timedwait的行为与pthread_cond_wait相同,返回时必须再次锁定互斥锁.
2. 2pthread_cond_timed等待所谓的return,如果它不等待条件变量,则不准确.
如果pthread_cond_timedwait超时,但此时关键部分无法锁定,则pthread_cond_timedwait不会立即返回,但是当pthread_cond_timedwait返回时,它仍位于关键部分中,此时的返回值为ETIMEDOUT.
对于pthread_cond_timedwait返回加班的问题,我也同意意见2.
附录:
int pthread_create(pthread_t *限制tidp,const pthread_attr_t * restrict_attr,void *(* start_rtn)(void *),void * restrict arg);
返回值: 如果成功,则返回0,否则返回错误号
返回成功时,将tidp指向的存储单元设置为新创建的线程的线程ID. attr参数用于制定各种线程属性. 新创建的线程从start_rtn函数的地址开始. 此函数只有一个无指针参数arg. 如果需要将多个参数传递给start_rtn函数,则需要将这些参数放入一个结构中,然后使用该结构的地址作为arg的参数.
C用于在Linux下开发多线程程序. Linux系统下的多线程遵循POSIX线程接口,称为pthreads.
装饰有限制的指针是最初访问该指针所指向的对象的唯一方法. 仅当第二个指针基于第一个指针时,才可以访问该对象. 对对象的访问仅限于由strict修改的指针表达式. 使用strict装饰的指针主要用于函数参数或指向由malloc()分配的内存空间. 限制数据类型不会更改程序的语义. 通过假设限制修改的指针是访问对象的唯一方法,编译器可以更好地优化某些类型的例程.
第一个参数是指向线程标识符的指针.
第二个参数用于设置线程属性.
第三个参数是线程运行函数的起始地址.
第四个参数是运行函数的参数.
由于pthread不是linux系统库,因此在编译时调用静态链接库时请注意添加-lpthread参数.
终止线程:
如果您在进程中的任何线程中调用exit或_exit,整个过程将终止,并且该线程的常规退出方法为:
(1)线程从启动例程返回(返回)
(2)线程可以被另一个进程杀死;
(3)线程自己调用pthread_exit函数
#include
pthread_exit
线程等待:
int pthread_join(pthread_t tid,void ** rval_ptr)
函数pthread_join用于等待线程结束. 函数原型为:
extern int pthread_join __P(pthread_t __th,void ** __ thread_return);
第一个参数是等待线程的标识符,第二个参数是用户定义的指针,可用于存储等待线程的返回值. 该功能是线程阻塞功能. 调用它的函数将等待,直到等待线程结束. 函数返回后,将恢复等待线程的资源.
从开源中文社区转移而来linux用不了多线程,其内容已被修改:
本文来自电脑杂谈,转载请注明本文网址:
https://www.pc-fly.com/a/jisuanjixue/article-254880-1.html
损我小米
只是美国这种做法有点可恶
干净