当前位置: 首页 > news >正文

8月4日建设部网站通报网站运营管理

8月4日建设部网站通报,网站运营管理,夏天做啥网站能致富,福建西南建设有限公司网站多线程线程线程的优点C语言多线程创建线程终止线程连接和分离线程开启一个线程最基本的多线程实现开启两个线程多线程进行协同运算无参数传递的线程并发编程实例简单参数传递的线程并发编程实例结构体参数传递的线程并发编程实例线程的连接编程实例信号量同步进行写入互斥信号量…

多线程

  • 线程
    • 线程的优点
  • C语言多线程
    • 创建线程
    • 终止线程
    • 连接和分离线程
  • 开启一个线程
    • 最基本的多线程实现
  • 开启两个线程
  • 多线程进行协同运算
      • 无参数传递的线程并发编程实例
      • 简单参数传递的线程并发编程实例
      • 结构体参数传递的线程并发编程实例
      • 线程的连接编程实例
      • 信号量同步进行写入
      • 互斥信号量实现对临界资源操作
    • 并发程序引起的共享内存的问题

在串口助手编程中,-k命令下需要实现等待接收message的同时可以发送键入message。但是,键入message使用的fgets()函数如果得不到键入就会一直等待,无法继续接收message,考虑采用多线程实现有键入则发送,否则一直等待接收message。

线程

线程在Unix系统下,通常被称为轻量级的进程,线程虽然不是进程,但却可以看作是Unix进程的表亲,同一进程中的多条线程将共享该进程中的全部系统资源,如虚拟地址空间,文件描述符和信号处理等等。但同一进程中的多个线程有各自的调用栈(call stack),自己的寄存器环境(register context),自己的线程本地存储(thread-local storage)。 一个进程可以有很多线程,每条线程并行执行不同的任务。

线程的优点

线程可以提高应用程序在多核环境下处理诸如文件I/O或者socket I/O等会产生堵塞的情况的表现性能。在Unix系统中,一个进程包含很多东西,包括可执行程序以及一大堆的诸如文件描述符地址空间等资源。在很多情况下,完成相关任务的不同代码间需要交换数据。如果采用多进程的方式,那么通信就需要在用户空间和内核空间进行频繁的切换,开销很大。但是如果使用多线程的方式,因为可以使用共享的全局变量,所以线程间的通信(数据交换)变得非常高效。

C语言多线程

多线程是多任务处理的一种特殊形式,多任务处理允许让电脑同时运行两个或两个以上的程序。一般情况下,两种类型的多任务处理:基于进程和基于线程。

基于进程的多任务处理是程序的并发执行。
基于线程的多任务处理是同一程序的片段的并发执行。
多线程程序包含可以同时运行的两个或多个部分。这样的程序中的每个部分称为一个线程,每个线程定义了一个单独的执行路径。

本教程假设您使用的是 Linux 操作系统,我们要使用 POSIX 编写多线程 C++ 程序。POSIX Threads 或 Pthreads 提供的 API 可在多种类 Unix POSIX 系统上可用,比如 FreeBSD、NetBSD、GNU/Linux、Mac OS X 和 Solaris。

创建线程

首先,c语言的多线程并发,需要用到 pthread.h 库。

#include <pthread.h>
For example: - pthread_t thrd1; - pthread_attr_t attr; - void thread_function(void argument); - char *some_argument;pthread_create(&thrd1, NULL, (void *)&thread_function, (void *) &some_argument)

线程创建函数包含四个变量,分别为: 1. 一个线程变量名,被创建线程的标识 2. 线程的属性指针,缺省为NULL即可 3. 被创建线程的程序代码 4. 程序代码的参数

创建一个POSIX 线程:

pthread_create (thread, attr, start_routine, arg)

pthread_create 创建一个新的线程,并让它可执行。

参数描述
thread指向线程标识符指针。
attr一个不透明的属性对象,可以被用来设置线程属性。您可以指定线程属性对象,也可以使用默认值 NULL。
start_routine线程运行函数起始地址,一旦线程被创建就会执行。
arg运行函数的参数。它必须通过把引用作为指针强制转换为 void 类型进行传递。如果没有传递参数,则使用 NULL。

创建线程成功时,函数返回 0,若返回值不为 0 则说明创建线程失败。

终止线程

pthread_exit(void *retval); //retval用于存放线程结束的退出状态

终止一个 POSIX 线程:

#include <pthread.h>
pthread_exit (status)

pthread_exit 用于显式地退出一个线程。通常情况下,pthread_exit() 函数是在线程完成工作后无需继续存在时被调用。

如果 main() 是在它所创建的线程之前结束,并通过 pthread_exit() 退出,那么其他线程将继续执行。否则,它们将在 main() 结束时自动被终止。

连接和分离线程

pthread_create调用成功以后,新线程和老线程谁先执行,谁后执行用户是不知道的,这一块取决与操作系统对线程的调度,如果我们需要等待指定线程结束,需要使用pthread_join函数,这个函数实际上类似与多进程编程中的waitpid。 举个例子,以下假设 A 线程调用 pthread_join 试图去操作B线程,该函数将A线程阻塞,直到B线程退出,当B线程退出以后,A线程会收集B线程的返回码。 该函数包含两个参数:

pthread_t th //th是要等待结束的线程的标识
void **thread_return //指针thread_return指向的位置存放的是终止线程的返回状态。
pthread_join (threadid, status) 
pthread_detach (threadid)

pthread_join() 子程序阻碍调用程序,直到指定的 threadid 线程终止为止。当创建一个线程时,它的某个属性会定义它是否是可连接的(joinable)或可分离的(detached)。只有创建时定义为可连接的线程才可以被连接。如果线程创建时被定义为可分离的,则它永远也不能被连。pthread_join() 函数来等待线程的完成。

开启一个线程

最基本的多线程实现

#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>void* func(void *args){printf("hello\n");return NULL;
}int main(){pthread_t th;pthread_create(&th, NULL, func, NULL);pthread_join(th, NULL);return 0;
}

主要分为三步:

  1. 声明一个线程变量th,类型为pthread_t
  2. 使用pthread_create函数创建,第一个参数是线程变量的地址,第三个参数是线程执行的函数
  3. pthread_join函数等待;

注意:pthread库不是Linux系统默认的库,连接时需要使用库libpthread.a, 在使用pthread_create创建线程时,在编译中要加-lpthread参数:gcc xxx.c -lpthread -o xxx.o ./xxx

开启两个线程

#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>void* func(void *args){int i;for(i=1; i<500; i++){printf("%d\n", i);}return NULL;
}int main(){pthread_t th1;pthread_t th2;pthread_create(&th1, NULL, func, NULL);pthread_create(&th2, NULL, func, NULL);pthread_join(th1, NULL);pthread_join(th2, NULL);return 0;
}

两个线程同时执行func函数,错序打印1~499
如果用到pthread_create函数的第4个参数,这个参数的传入会反应到func中的形参中去。

void* func(void *args){int i;char *name = (char*)args;for(i=1; i<500; i++){printf("%s:%d\n", name, i);}return NULL;
}int main(){pthread_t th1;pthread_t th2;pthread_create(&th1, NULL, func, "th1");pthread_create(&th2, NULL, func, "th2");pthread_join(th1, NULL);pthread_join(th2, NULL);return 0;
}

输出的结果,我们可以清晰地看出th1和th2的线程标记和交错运行。

多线程进行协同运算

创建一个数组,其中有5000个元素,我们想用两个线程来共同计算这5000个元素的加法和。

int arr[5000];
int s1 = 0;
int s2 = 0;
void *func1(void *args){int i;char *name = (char *)args;for(i = 1; i < 2500; i++){s1 += arr[i];}return NULL;
}
void *func1(void *args){int i;char *name = (char *)args;for(i = 2500; i < 5000; i++){s2 += arr[i];}return NULL;
}

从两个线程的函数可以看出,一个线程计算前2500个值的加法和,另一个线程计算后2500个值的加法和。

int main(){int i;for(i = 0; i <5000; i++){arr[i]  =  rand() % 50;}pthread_ th1;pthread_t th2;pthread_create(&th1, NULL, func1,  NULL);pthread_create(&th2, NULL, func2, NULL);pthread_join(th1, NULL);pthread_join(th2, NULL);printf("s1 = %d, s2 = %d, s1+s2 = %d\n", s1, s2, s1+s2);return 0;
}

main函数中,在pthread_join函数等待的th1th2都结束后,输出对应的值。

代码里,th1和th2的执行函数中有大量的相似代码,所以我们最后用一个函数来复用。不难想到,需要通过传参的方式来实现代码复用。这里我们定义一个结构体,结构体中有循环的起始标记first,终止标记last,区间内加法和result。

typedef struct{int first;int last;int result;
}MY_ARGS;
int arr[5000];
void *func(void *args){int i;int s = 0;//参数强制类型转换MY_ARGS *my_args = (MY_ARGS *)args;for(i = my_args->first; i < my_args->last; i++){s += arr[i];}my_args->result = s;return NULL;
}

func1fun2整合到了func中去。而在main函数中,我们创建线程的时候传入的参数正是结构体指针:

int main(){int i;for(i = 0; i < 5000; i++){arr[i] = rand() % 50;}pthread_t th1;pthread_t th2;MY_ARGS args1 = {0, 2500, 0};MY_ARGS args2 =  {2500, 5000, 0};pthread_create(&th1, NULL, func, &args1);pthread_create(&th2, NULL, func, &args2);pthread_join(th1, NULL);pthread_join(th2, NULL);printf("s1 = %d, s2 = %d, s1+s2 = %d\n", args1.result, args2.result, args1.result+args2.result);return 0;
}

这样在func函数中,我们就可以对传入的结构体参数中的元素进行利用了,将计算所得传到结构体的result中去。这样我们输出加法和,就可以得到跟上面一样的结果,但是代码会更整洁漂亮

无参数传递的线程并发编程实例

// 基于线程的并发编程
#include <stdio.h>
#include <pthread.h>
#define NUM_Threads 5// 线程的运行函数
void *PrintHello(void *arg)
{printf("Hello,World of Thread in C!\n");return 0;
}int main()
{int i;int ret;// 定义线程的id变量,多个变量使用数组pthread_t tids[NUM_Threads];for (i=0; i<NUM_Threads; i++){// 参数依次是: 创建的线程id,线程参数,调用的函数,传入的函数参数ret = pthread_create(&tids[i], NULL, PrintHello, NULL);if (ret != 0){printf("pthread_create error: error_code = \n");}}// 等各个线程推出后,进程才结束pthread_exit(NULL);return 0;
}/** 在CLion(Ubuntu)中输出结果为
Hello,World of Thread in C!
Hello,World of Thread in C!
Hello,World of Thread in C!
Hello,World of Thread in C!
Hello,World of Thread in C!* */

简单参数传递的线程并发编程实例

// 基于线程的并发编程,向线程传递参数1
// Test_2_createThread
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#define NUM_Threads 5// 线程的运行函数
void *PrintHelloId(void *threadid)
{// 对传入的参数进行强制类型转换,由无类型指针变为整形指针,然后再读取int tid = *((int *)threadid);printf("Hello,World, Thread %d\n",tid);return 0;
}int main()
{pthread_t pthreads[NUM_Threads];int i, rc;// 用数组存储i的数值int indexes[NUM_Threads];for (i=0; i<NUM_Threads; i++){printf("main() : 创建线程 %d \n",i);indexes[i] = i; // 保存i的数值// 在indexes传入参数的时候必须转换为无类型指针rc = pthread_create(&pthreads[i], NULL, PrintHelloId, (void *)&indexes[i]);if (0 != rc){printf("Error: 无法创建线程!\n");exit(-1);}}pthread_exit(NULL);return 0;
}/** 在CLion(Ubuntu)中输出结果是
main() : 创建线程 0
main() : 创建线程 1
Hello,World, Thread 0
main() : 创建线程 2
Hello,World, Thread 1
main() : 创建线程 3
Hello,World, Thread 2
main() : 创建线程 4
Hello,World, Thread 3
Hello,World, Thread 4* */

结构体参数传递的线程并发编程实例

// 基于线程的并发编程,向线程传递参数2(传递结构体)
// Test_3_createThread
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#define NUM_Threads 5typedef struct thread_data{int threadid;char message;
}THDATA,*PTHDATA;void * PrintHello(void * pthreadid)
{PTHDATA tid = (PTHDATA)pthreadid;printf("This is Pthread : %d ;info : %c \n",tid->threadid, tid->message);return 0;
}int main(void)
{pthread_t Pthread[NUM_Threads];THDATA index[NUM_Threads];int i, ret;for (i = 0; i < NUM_Threads; i++){printf("main() : 创建线程 %d \n",i);index[i].threadid = i;index[i].message = 'A'+i%10;ret = pthread_create(&Pthread[i], NULL, PrintHello, (void *)&index[i]);if (0 != ret){printf("Error: 创建线程失败!\n");exit(-1);}}pthread_exit(NULL);return 0;
}/** 在CLion(Ubuntu)中输出结果是
main() : 创建线程 0
main() : 创建线程 1
This is Pthread : 0 ;info : A
main() : 创建线程 2
main() : 创建线程 3
This is Pthread : 2 ;info : C
main() : 创建线程 4
This is Pthread : 3 ;info : D
This is Pthread : 4 ;info : E
This is Pthread : 1 ;info : B* */

线程的连接编程实例

// 基于线程的并发编程,连接或分离线程
// Test_4_createThread
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>#define NUM_Pthread 5void *PrintHello(void * pthreadid)
{int tid = *((int *)pthreadid);printf("Sleeping in thread %d ,...exiting \n",tid);return 0;
}int main(void)
{int i, ret;pthread_t Pthread[NUM_Pthread];pthread_attr_t attr; // 定义线程属性void * status;int index[NUM_Pthread];// 初始化并设置线程为可连接pthread_attr_init(&attr);pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);for (i=0; i<NUM_Pthread; i++){printf("main() : 创建线程 %d \n",i);index[i] = i;ret = pthread_create(&Pthread[i], NULL, PrintHello, (void *)&index[i]);}// 删除属性,并等待其他线程pthread_attr_destroy(&attr);for (i=0; i<NUM_Pthread; i++){ret = pthread_join(Pthread[i], status);if (0 != ret){printf("Error: unable to join,%d\n",ret);exit(-1);}printf("main(): complete thread id : %d",i);printf(" exiting with status : %p\n",status);}printf("main() : program exiting.\n");pthread_exit(NULL);return 0;
}

信号量同步进行写入

// 用信号量进行同步
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <semaphore.h>#define Len 100       // 设置输入内容长度sem_t bin_sem;
char work_area[Len]; // 存放输入内容void *Thread_func(void *arg)
{// 等待信号量有大于0的值然后退出sem_wait(&bin_sem);while (0 != strncmp("end", work_area, 3)){printf("Input %ld characters\n", strlen(work_area)-1);}return 0;
}int main(void)
{int res;    // 存放命令的返回值pthread_t Pthread; // 创建线程void *thread_result; // 存放线程处理结果// 初始化信号量,并设置初始值为0res = sem_init(&bin_sem, 0, 0);if (0 != res){perror("Semaphore initialization failes");exit(EXIT_FAILURE);}// 创建新线程 0res = pthread_create(&Pthread, NULL, Thread_func, NULL);if (0 != res){perror("Thread creation failed");exit(EXIT_FAILURE);}printf("Enter 'end' to finish\n");// 当工作区内不是以end开头的字符串,则继续输入while (0 != strncmp("end", work_area, 3)){// 以标准输入获取输入到工作区内fgets(work_area, Len, stdin);sem_post(&bin_sem);  // 信号量+1}printf("\n Waiting for thread to finish...\n");// 等待线程结束res = pthread_join(Pthread, &thread_result);if (0 != res){perror("Thread join failed");exit(EXIT_FAILURE);}printf("Thread joined\n");sem_destroy(&bin_sem);  // 销毁信号量exit(EXIT_SUCCESS);return 0;
}

互斥信号量实现对临界资源操作

// 用互斥信号量进行同步
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>#define Len 3 // 自增计算次数
#define NUM_Pthread 5 // 设置线程的长度int count = 1; // 在数据段共享资源,多个进程抢占临界资源
// 对于临界资源,应该添加互斥锁
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;void *Thread_func(void *threadid)
{int tid = *((int *)threadid);int i, val;printf("Pthread ID : %d \n",tid);for (i=0; i<NUM_Pthread; i++){pthread_mutex_lock(&mutex);val = count;printf("val = %d \n",val++);count = val;pthread_mutex_unlock(&mutex);}return 0;
}int main(void)
{int res;    // 存放命令的返回值int i;pthread_t Pthread[NUM_Pthread]; // 创建线程int index[NUM_Pthread];for (i=0; i<NUM_Pthread; i++){index[i] = i;// 创建线程res = pthread_create(&Pthread[i], NULL, Thread_func, (void *)&index[i]);if (0 != res){printf("Error: 创建线程失败!\n");exit(-1);}}for (i=0; i<NUM_Pthread; i++){// 汇合线程pthread_join(Pthread[i], NULL);}printf("count = %d\n",count);pthread_exit(NULL);return 0;
}// 在运行此程序无互斥锁时,我们不仅得到错误的答案,而且每次得到的答案都不相同
// 分析
// 当多个对等线程在一个处理器上并发运行时,机器指令以某种顺序完成,每个并发执行定义了线程中指令的某种顺序

并发程序引起的共享内存的问题

有两个进程,两个进程共享全局变量s。两个进程都执行一个计数功能的函数,直观地看过去,th1运行时s++要执行10000次,th2运行时s++也要执行10000次,似乎计算得到的最后s应该是20000。但实际上是这样的吗?

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
int s = 0;
void *func(void*args){int i;for(i = 0; i < 10000; i++){s++;}return NULL;
}
int main(){pthread_t th1;pthread_t th2;pthread_create(&th1, NULL, func, NULL);pthread_create(&th2, NULL, func, NULL);pthread_join(th1, NULL);pthread_join(th2, NULL);printf("%s = %d\n", s);return 0;
}

编译运行后,发现输出并不是20000,而是12657。

原因:当我们执行s++,底层发生的事件其实是:内存中读取s→将s+1→将s写入到内存。这不是一个原子化操作,当两个线程交错运行的时候,很容易发生结果的丢失。因此最后的结果肯定是要小于20000的。这种情况有种专有名词,叫race condition。

为了解决这个问题,我们可以加锁。

#include <pthread.h>
int s = 0;
pthread_mutex_t lock;	//锁的声明
void *func(void *args){int i;for(i = 0; i < 10000; i++){	//给临界区代码加锁实现原子化操作pthread_mutex_lock(&lock);s++;pthread_mutex_unlock(&lock);}return NULL;
}
int main(){pthread_t th1;pthread_t th2;//锁初始化pthread_mutex_init(&lock, NULL);pthread_create(&th1, NULL, func, NULL);pthread_createe(&th2, NULL, func, NULL);pthread_join(th1, NULL);pthread_join(th2, NULL);printf("s = %d\n, s);return 0;
}

改进后的代码如下,学过操作系统会很好理解,无非就是为了保证共享内存区(临界区)的原子化操作,我们可以在进这段代码之前加锁(pthread_mutex_lock),意味着其他线程看到这段内存被其他人占有的时候,就不去抢占,等这段内存被解锁(pthread_mutex_unlock)之后,它才有读写这段临界区的权利。

但其实这种方式的执行速度并不快,比如这段代码里,每个线程都要进行10000次加解锁的操作,它能解决内存读写冲突的问题,但是却牺牲了效率。

http://www.fp688.cn/news/164587.html

相关文章:

  • 部门门户网站建设请示曹操博客seo
  • 闵行网站制作设计公司b2b外贸接单平台
  • 做pcr查基因序列的网站龙岗网站建设公司
  • 深圳找工作哪个网站好地推怎么做最有效
  • 东莞手机网站制作免费开发网站
  • 学什么可以做视频网站下载百度app
  • 单页营销网站设计邯郸seo优化
  • 网络网站公司seo文章排名优化
  • 旅游网站制作毕业论文郑州seo招聘
  • 关键词排名优化网站市场调研与分析
  • 哪个网站做处理货事件营销的案例有哪些
  • 西安旅游必去十大景点推荐网站页面优化内容包括哪些
  • 黑河做网站的公司关键词排名优化易下拉技术
  • 长沙B2B2C商城网站建设设计公司排名
  • 网站开发设计招聘宁波网站推广公司价格
  • 怎么自己建网站赚钱出售友情链接是什么意思
  • wordpress成品网站免费seo怎么做最佳
  • 国外数据网站磁力
  • 怎样做中英文网站百度推广营销
  • 网站建设时间查询矿坛器材友情交换
  • wordpress怎么编辑页面百度竞价关键词怎么优化
  • 亚马逊虚拟主机做网站云seo关键词排名优化软件
  • dw怎么做网站注册登入页面某网站seo诊断分析
  • 企业网站系统百度网盘客服24小时电话人工服务
  • 简述app开发流程seo结算系统
  • 兰州专业做网站的公司哪家好b站视频推广的方法有哪些
  • 网站怎么在工信部备案上海城市分站seo
  • 湖南网站开发公司电话网站收录查询平台
  • 华为云怎么做网站关键词百度指数查询
  • 东莞品牌做网站搜索引擎优化宝典