对线程的深入学习(二)

1 .验证怎么向线程中传递参数,使用返回值:

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

#define PAI 3.14159

void* thread_area (void* arg) { //计算面积
	double r = *(double*)arg;
	*(double*)arg = PAI * r * r;//用输出参数把值带回到主函数
	return NULL;
}
int main (void) {
	printf ("r = ");
	double rs;
	scanf ("%lf", &rs);//输入半径

	pthread_t tid;
	int error = pthread_create (&tid, NULL, thread_area, &rs);//注意这时候传的是rs的地址,而不是直接传值
	if (error) {
		fprintf (stderr, "pthread_create: %s\n", strerror (error));
		return -1;
	}

	if ((error = pthread_join (tid, NULL)) != 0) {
		fprintf (stderr, "pthread_join: %s\n", strerror (error));
		return -1;
	}

	printf ("s = %g\n", rs);//这个时候就能保证子线程已经被执行,现在rs里边装的就一定是正确的面积

	return 0;
}

注意:pthread_join有两个作用:一个是回收线程资源,另一个是等待子线程结束与主线程汇合,如果没有这个函数直接在pthread_create打印的话,由于由于父子执行顺序是不定的,所以我们不一定能得到正确的面积,

//参数arg是结构体的情况
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <unistd.h>
#include <pthread.h>

#define PAI 3.14159

typedef struct tag_Pyth {//两个直角边,和一个斜边
	double a;
	double b;
	double c;
}	PYTH, *LPPYTH;

void* thread_pyth (void* arg) {
	LPPYTH pyth = (LPPYTH)arg;
	pyth -> c = sqrt (pyth -> a * pyth -> a + pyth -> b * pyth -> b);// a方加b方再开方
	return NULL;
}
int main (void) {
    int error;
    pthread_t tid;
	PYTH pyth;//读入两条边的值 计算另外一条的值
	printf ("a = ");
	scanf ("%lf", &pyth.a);
	printf ("b = ");
	scanf ("%lf", &pyth.b);

	if ( error = pthread_create (&tid, NULL, thread_pyth, &pyth) != 0) {
		fprintf (stderr, "pthread_create: %s\n", strerror (error));
		return -1;
	}
	if(error = pthread_join (tid, NULL) != 0) {
		fprintf (stderr, "pthread_join: %s\n", strerror (error));
		return -1;
	}

	printf ("c = %g\n", pyth.c);  

	return 0;
}

//用返回值的方法把圆的面积带回到主线程
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>

#define PAI 3.14159

void* thread_area (void* arg) {
	double r = *(double*)arg;
	/*
    double s;
	s = PAI * r * r;
	return &s;
	*/
	double* s = malloc (sizeof (double));
	*s = PAI * r * r;
	return s;
}

int main (void) {
	printf ("r = ");
	double r;
	scanf ("%lf", &r);

	pthread_t tid;
	int error = pthread_create (&tid, NULL, thread_area, &r);
	if (error) {
		fprintf (stderr, "pthread_create: %s\n", strerror (error));
		return -1;
	}

	double* x;
	if ((error = pthread_join (tid, (void**)&x)) != 0) {
		fprintf (stderr, "pthread_join: %s\n", strerror (error));
		return -1;
	}

	printf ("x = %g\n", *x);
	free (s);   

	return 0;
}

注意:thread_area 函数中的s变量不能是局部变量,会导致返回无效,当然我们可以将s定义为static类型,但是如果是静态变量是被各个线程共享的,会导致并发冲突。最后我们采用malloc申请一块堆内存,这里的s是指针,存贮在栈内存中,而每个线程都有自己独立的栈空间,所以这个方案既不会有返回无效问题也不存在并发冲突。这里的*s是堆内存,所以可以在子线程外边free掉。

2. 终止线程

void pthread_exit (void* retval);

retval - 和线程过程函数的返回值语义相同。

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

#define PAI 3.14159

void* thread_area (void* arg) {
	double r = *(double*)arg;
	double* s = malloc (sizeof (double));
//	exit (0);
	*s = PAI * r * r;
	pthread_exit (s); // <==> return s;
	*s = 2 * PAI * r;//这两行根本执行不到,所以对s的值不会产生任何影响
	return s;
}

int main (void) {
	printf ("r = ");
	double r;
	scanf ("%lf", &r);

	pthread_t tid;
	int error = pthread_create (&tid, NULL, thread_area, &r);
	if (error) {
		fprintf (stderr, "pthread_create: %s\n", strerror (error));
		return -1;
	}

	double* s;
	if ((error = pthread_join (tid, (void**)&s)) != 0) {
		fprintf (stderr, "pthread_join: %s\n", strerror (error));
		return -1;
	}
	printf ("s = %g\n", *s);
	free (s);
	return 0;
}

所以程序的结果还是打印圆的面积而非周长,这里需要慎用exit函数,因为在任何线程中调用exit函数都将终止整个进程。

3 . 取消线程

1) 向指定线程发送取消请求

int pthread_cancel (pthread_t thread);

成功返回0,失败返回错误码。

注意:该函数只是向线程发出取消请求,并不等待线程终止。 缺省情况下,线程在收到取消请求以后,并不会立即终止,而是仍继续运行,直到其达到某个取消点。在取消点处,线程检查其自身是否已被取消了,并做出相应动作。当线程调用一些特定函数时,取消点会出现,对可能引发阻塞的函数会检查取消点。

2) 设置调用线程的可取消状态

int pthread_setcancelstate (int state,  int* oldstate);

成功返回0,并通过oldstate参数输出原可取消状态   (若非NULL),失败返回错误码。

state取值:

PTHREAD_CANCEL_ENABLE  - 接受取消请求(缺省)。

PTHREAD_CANCEL_DISABLE - 忽略取消请求。

3) 设置调用线程的可取消类型

int pthread_setcanceltype (int type, int* oldtype);

成功返回0,并通过oldtype参数输出原可取消类型  (若非NULL),失败返回错误码。

type取值:

PTHREAD_CANCEL_DEFERRED     - 延迟取消(缺省)。

被取消线程在接收到取消请求之后并不立即响应,而是一直等到执行了特定的函数(取消点)之后再响应该请求。

PTHREAD_CANCEL_ASYNCHRONOUS - 异步取消。

被取消线程可以在任意时间取消,不是非得遇到取消点才能被取消。但是操作系统并不能保证这一点。

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

void elapse (void) {
	size_t i;
	for (i = 0; i < 800000000; ++i);
}

void* thread_proc (void* arg) {
	/*
	int error = pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, NULL);     //敲回车,不会终止
	if (error) {
		fprintf (stderr, "pthread_setcancelstate: %s\n", strerror (error));
		exit (EXIT_FAILURE);
	}
	*//*
	int error = pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL);//敲回车后直接终止,但不是总能有效
	if (error) {
		fprintf (stderr, "pthread_setcanceltype: %s\n", strerror (error));
		exit (EXIT_FAILURE);
	}
	*/
	for (;;) {
		printf ("线程:子在川上曰,逝者如斯夫。\n");
		elapse ();
	}

	return NULL;
}

int main (void) {
	setbuf (stdout, NULL);
	printf ("按<回车>取消线程...\n");

	pthread_t tid;
	int error = pthread_create (&tid, NULL, thread_proc, NULL);
	if (error) {
		fprintf (stderr, "pthread_create: %s\n", strerror (error));
		return -1;
	}

	getchar ();
    //默认的是延迟终止线程
	if ((error = pthread_cancel (tid)) != 0) {
		fprintf (stderr, "pthread_cancel: %s\n", strerror (error));
		exit (EXIT_FAILURE);
	}

	printf ("已发送取消请求,等待线程终止...\n");

	if ((error = pthread_join (tid, NULL)) != 0) {
		fprintf (stderr, "pthread_join: %s\n", strerror (error));
		return -1;
	}
	printf ("线程已终止。\n");
	return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-09 02:05:42

对线程的深入学习(二)的相关文章

获取当前线程的对象学习二

Thread.currentThread(), 主线程也可以获取 * new Thread(new Runnable() { public void run() { for(int i = 0; i < 1000; i++) { System.out.println(Thread.currentThread().getName() + "...aaaaaaaaaaaaaaaaaaaaa"); } } }).start(); new Thread(new Runnable() {

Struts2框架学习(二) Action

Struts2框架学习(二) Action Struts2框架中的Action类是一个单独的javabean对象.不像Struts1中还要去继承HttpServlet,耦合度减小了. 1,流程 拦截器拦截请求,创建代理Action对象,执行方法,返回结果,界面跳转. 拦截器解析请求路径,获取Action的名称,到配置文件中查找action的完整类名,利用反射创建对象. 每请求一次,就创建一个对象,所以action是多例的,也是线程安全的. 2,关系 请求的路径和配置文件的对应关系: 配置文件中包

[Python 学习] 二、在Linux平台上使用Python

这一节,主要介绍在Linux平台上如何使用Python 1. Python安装. 现在大部分的发行版本都是自带Python的,所以可以不用安装.如果要安装的话,可以使用对应的系统安装指令. Fedora系统:先以root登入,运行 yum install python Ubuntu系统:在root组的用户, 运行 sudo apt-get install python 2. 使用的Python的脚本 Linux是一个以文件为单位的系统,那么我们使用的Python是哪一个文件呢? 这个可以通过指令

OpenCV for Python 学习 (二 事件与回调函数)

今天主要看了OpenCV中的事件以及回调函数,这么说可能不准确,主要是下面这两个函数(OpenCV中还有很多这些函数,可以在 http://docs.opencv.org/trunk/modules/highgui/doc/user_interface.html 找到,就不一一列举了),然后自己做了一个简单的绘图程序 函数如下: cv2.setMouseCallback(windowName, onMouse[, param]) cv2.createTrackbar(trackbarName,

Makefile持续学习二

Makefile概述 一.Makefile里有什么? Makefile里主要包含5个东西:显式规则.隐晦规则.变量定义.文件指示和注释 1.显式规则:显式规则说明如恶化生成一个或多的目标文件,包含要生成的文件,文件的依赖文件,生成的命令 2.隐晦规则:由make自动推动功能完成 3.变量定义:变量一般都是字符串,类似C语言中的宏定义,当Makefile被执行时,其中的变量都会被扩展到相应的引用位置上 4.文件指示: 在一个Makefile中引用另一个Makefile 根据某些情指定Makefil

redis ruby客户端学习( 二)

接上一篇redis ruby客户端学习( 二) 对于redis的五种数据类型:字符串(String), 哈希(Map), 列表(list), 集合(sets) 和 有序集合(sorted sets),上一篇介绍了字符串. 1,哈希(Map) hset.设置 key 指定的哈希集中指定字段的值.如果 key 指定的哈希集不存在,会创建一个新的哈希集并与 key 关联.如果字段在哈希集中存在,它将被重写. require "redis" r = Redis.new r.hset 'my_h

Duilib学习二 第一个程序 Hello World

Duilib学习二  第一个程序 Hello World #pragma once #include <UIlib.h> using namespace DuiLib; #ifdef _DEBUG # ifdef _UNICODE # pragma comment(lib, "DuiLib_ud.lib") # else # pragma comment(lib, "DuiLib_d.lib") # endif #else # ifdef _UNICOD

Jquery Easy UI初步学习(二)datagrid的使用

第一篇学的是做一个管理的外框,接着就是数据datagrid绑定了,这里我用asp.net mvc3来做的,主要就是熟悉属性.方法. 打开easyui的demo 就可以看到如下一段代码: 和上篇一样class="easyui-datagrid", data-options="...",这是一样的,其他我在网上查了查,并做了整理 DataGrid 属性 参数名 类型 描述 默认值 title string Datagrid面板的标题 null iconCls strin

Oracle学习(二):过滤和排序

1.知识点:可以对照下面的录屏进行阅读 SQL> --字符串大小写敏感 SQL> --查询名叫KING的员工信息 SQL> select * 2 from emp 3 where ename = 'KING'; SQL> --日期格式敏感 SQL> --查询入职日期为17-11月-81的员工 SQL> select * 2 from emp 3 where hiredate='17-11月-81'; --正确例子 SQL> ed 已写入 file afiedt.b

Jetty学习二:配置概览-怎么配置Jetty

Jetty POJO配置 Jetty的核心组件是Plain Old Java Objects(POJOs):配置Jetty的大部分工作就是在Jetty POJOs上的初始化.装配和设置域的处理,你能通过以下的方式来实现:  1)直接通过Java代码初始化和装配Jetty对象.这个在后面Embedding Jetty讲. 2)用Jetty XML配置(一个控制反转(IoC)框架)初始化和装配Jetty对象.etc/jetty.xml文件是基本的Jetty XML配置文件,但有一些其它的etc/je