线程管理 - d15

一、基本概念

  • 1.线程就是进程中代码的执行路线,即进程内部的控制序列。
程序――磁盘文件,纸面上的旅行日程表
进程――内存数据,头脑中的旅行计划
线程――执行路径,按照计划旅行
  • 2.线程只是一个执行路径,并不拥有独立的内存资源,
共享进程的代码区、数据区、堆区、命令行参数和环境变量。
  • 3.线程拥有自己独立的栈,因此用户自己独立的局部变量。
  • 4.一个进程可以同时拥有多个线程,但是至少有一个主线程。

二、POSIX线程

IEEE POSIX 1003.1c指定了POSIX线程标准。

// pthread
#include <pthread.h>
# libpthread.so
gcc ... -lpthread

三、POSIX线程库的功能

  • 1.线程管理:创建、销毁、分离、汇合、获取或者设置属性。
  • 2.线程同步:解决并发冲突问题。

四、线程函数

1.创建(工作)线程

int pthread_create (
      pthread_t* tid,                 // 线程ID
      const pthread_attr_t* attr,     // 线程属性,置NULL表示缺省属性
      void* (*start_routine) (void*), // 线程过程函数
      void* arg                       // 传递给线程过程函数的参数
);


/* 内核:开辟一个新的线程,在该线程中调用
  start_routine (arg);
*/

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

2.汇合线程

int pthread_join (
      pthread_t tid,   // 所要汇合的线程ID
      void**    retval // 线程过程函数的返回值
);

// 线程版的waitpid。
// 该函数会一直阻塞,直到tid参数所标识的线程结束(线程过程函数返回),成功返回0,失败返回错误码。
// 如果对线程过程函数的返回值不感兴趣,retval可以置NULL。

3.获取线程自身的ID

pthread_t pthread_self (void);

//成功返回调用线程的ID,不会失败。

4.比较线程ID

int pthread_equal (pthread_t t1, pthread_t t2);

// t1和t2相等返回非0,否则返回0。

5.终止线程(线程自杀)

void pthread_exit (void* retval);
// 线程版的exit。
/*
void fun2 (void) {
  if (我实在受不了了)
    exit (-1);
}

void fun1 (void) {
  fun2 ();
}

int main (void) {
   fun1 ();
   return 0;
}
*/

6.分离线程

默认情况下的线程都是可汇合线程,即可以通过pthread_join汇合的线程。 这样的线程即便终止,系统仍然会为其保留部分资源(其中包括线程过程函数的返回值),直到调用pthread_join函数,这部分资源才会被回收。

调用如下函数:

pthread_detach (pthread_t tid);
// 将tid参数所标识的线程置为分离线程。这样的线程不可被pthread_join汇合,线程终止以后其全部资源被系统自动回收。

7.取消线程(线程他杀)

int pthread_cancel (pthread_t tid);
// 成功返回0,失败返回错误码。
// 设置调用线程的取消状态:
int pthread_setcancelstate (int state, int* oldstate);
/*
state取值:
PTHREAD_CANCEL_ENABLE  - 可以取消(默认)
PTHREAD_CANCEL_DISABLE - 禁止取消
*/
// 设置调用线程的取消类型:
int pthread_setcanceltype (int type, int* oldtype);
/*
type取值:
PTHREAD_CANCEL_DEFERRED - 延迟取消(默认)
PTHREAD_CANCEL_ASYNCHRONOUS - 立即取消
*/

网络通信补遗

#include <sys/socket.h>
ssize_t recvfrom (int sockfd,
                   void* buf,
                   size_t len,
                   int flags,
                  struct sockaddr* addr,
                  socklen_t* addrlen);
// 输入数addr和addrlen表示发送方的地址信息。

ssize_t sendto ( int sockfd,
                 void* buf,
                 size_t len,
                 int flags,
                 struct sockaddr const* addr,
                 socklen_t addrlen);

// 输入参数addr和addrlen表示接收方的地址信息。

example

/*
 * create.c
 */

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
void* thread_proc (void* arg) {
	for (;;) {
		printf ("%c", (char)arg);
		usleep (100000);
	}
	return NULL;
}
int main (void) {
	setbuf (stdout, NULL);
	pthread_t tid;
	int error = pthread_create (&tid, NULL, thread_proc, (void*)'A');
	if (error) {
		printf ("pthread_create: %s\n", strerror (error));
		return -1;
	}
	printf ("线程ID:%lu\n", tid);
	error = pthread_create (&tid, NULL, thread_proc, (void*)'B');
	if (error) {
		printf ("pthread_create: %s\n", strerror (error));
		return -1;
	}
	printf ("线程ID:%lu\n", tid);
	thread_proc ((void*)'C');
	//getchar ();
	return 0;
}
/*
 * join.c
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#define PI 3.1415926

double g_s;
void* area (void* arg) {
	double r = *(double*)arg;
	/*
	static double s;
	s = PI * r * r;
	return &s;
	*//*
	double* s = malloc (sizeof (double));
	*s = PI * r * r;
	return s;
	*/
	g_s = PI * r * r;
	return NULL;
}
int main (void) {
	double r = 10;
	pthread_t tid;
	pthread_create (&tid, NULL, area, &r);
	/*
	double* s;
	pthread_join (tid, (void**)&s); // *s = ...
	printf ("%g\n", *s);
	free (s);
	*/
	//sleep (1);
	pthread_join (tid, NULL);
	printf ("%g\n", g_s);
	return 0;
}
/*
 * exit.c
 */

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

#define PI 3.1415926

void* area (void* arg) {
	double r = *(double*)arg;
	double* s = malloc (sizeof (double));
	*s = 2 * PI * r;
	pthread_exit (s);
	*s = PI * r * r;
	return s;
}
int main (void) {
	//pthread_exit (NULL);
	double r = 10;
	pthread_t tid;
	pthread_create (&tid, NULL, area, &r);
	double* s;
	pthread_join (tid, (void**)&s); // *s = ...
	printf ("%g\n", *s);
	free (s);
	return 0;
}
/*
 * equal.c
 */

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

pthread_t g_main;
void* ismain (void* arg) {
	if (pthread_equal (pthread_self (), g_main)){
		printf ("这是主线程。\n");
	}
	else{
		printf ("这是子线程。\n");
	}
	return NULL;
}
int main (void) 
{
	g_main = pthread_self ();
	ismain (NULL);
	pthread_t tid;
	pthread_create (&tid, NULL, ismain, NULL);
	pthread_join (tid, NULL);
	return 0;
}
/*
 * detach.c
 */

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

void* thread_proc (void* arg) {
	//pthread_detach (pthread_self ());
	int i;
	for (i = 0; i < 200; ++i) {
		putchar ('-');
		usleep (50000);
	}
	return NULL;
}
int main (void) {
	setbuf (stdout, NULL);
	pthread_t tid;
	pthread_create (&tid, NULL, thread_proc, NULL);
	pthread_detach (tid);
	int error = pthread_join (tid, NULL);
	if (error){
		printf ("pthread_join: %s\n", strerror (error));
	}
	int i;
	for (i = 0; i < 200; ++i) {
		putchar ('+');
		usleep (50000);
	}
	printf ("\n");
	return 0;
}
/*
 * cancle.c
 */

#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) {
	/*
	pthread_setcancelstate ( PTHREAD_CANCEL_DISABLE, NULL);
	*/
	pthread_setcanceltype ( PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
	for (;;) {
		printf ("我是快乐的子线程!\n");
		elapse ();
	}
	return NULL;
}
int main (void) {
	setbuf (stdout, NULL);
	pthread_t tid;
	pthread_create (&tid, NULL, thread_proc, NULL);
	getchar ();
	int error = pthread_cancel (tid);
	if (error){
		printf ("pthread_cancel: %s\n", strerror (error));
	}
	pthread_join (tid, NULL);
	printf ("子线程终止!\n");
	return 0;
}

/*
 * udpcli.c
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main (int argc, char* argv[]) {
	if (argc < 3) {
		printf ("用法:%s <IP地址> <端口号>\n", argv[0]);
		return -1;
	}
	printf ("客户机:创建套接字...\n");
	int sockfd = socket (AF_INET, SOCK_DGRAM, 0);
	if (sockfd == -1) {
		perror ("socket");
		return -1;
	}
	printf ("客户机:准备地址并连接...\n");
	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_port = htons (atoi (argv[2]));
	addr.sin_addr.s_addr = inet_addr (argv[1]);
	/*
	if (connect (sockfd,
		(struct sockaddr*)&addr,
		sizeof (addr)) == -1) {
		perror ("connect");
		return -1;
	}
	*/
	printf ("客户机:发送数据...\n");
	for (;;) {
		printf ("> ");
		char buf[1024];
		gets (buf);
		if (! strcmp (buf, "!"))
			break;
		if (sendto (sockfd, buf,
			strlen (buf) + 1, 0,
			(struct sockaddr*)&addr,
			sizeof (addr)) == -1) {
			perror ("sendto");
			return -1;
		}
		if (! strcmp (buf, "!!"))
			break;
	}
	printf ("客户机:关闭套接字...\n");
	close (sockfd);
	return 0;
}
/*
 * tcpcli.c
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main (int argc, char* argv[]) 
{
	if (argc < 3) {
		printf ("用法:%s <IP地址> <端口号>\n", argv[0]);
		return 0;
	}

	printf ("客户机:创建套接字...\n");
	int sockfd = socket (AF_INET, SOCK_STREAM, 0);
	if (sockfd == -1) {
		perror ("socket");
		return -1;
	}

	printf ("客户机:准备地址并连接...\n");
	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_port = htons (atoi (argv[2]));
	addr.sin_addr.s_addr = inet_addr (argv[1]);
	if (connect (sockfd, (struct sockaddr*)&addr, sizeof (addr)) == -1) {
		perror ("connect");
		return -1;
	}

	printf ("客户机:收发数据...\n");
	for (;;) {
		printf ("> ");
		char buf[1024];
		gets (buf);
		if (! strcmp (buf, "!")) break;

		if (send (sockfd, buf, strlen (buf) + 1, 0) == -1) {
			perror ("send");
			return -1;
		}

		memset (buf, 0, sizeof (buf));
		ssize_t rb = recv (sockfd, buf, sizeof (buf), 0);
		if (rb == -1) {
			perror ("recv");
			return -1;
		}
		if (rb == 0) {
			printf ("客户机:服务器已宕机!\n");
			break;
		}
		printf ("< %s\n", buf);
	}
	printf ("客户机:关闭套接字...\n");
	close (sockfd);
	return 0;
}
/*
 * tcpcli.c
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main (int argc, char* argv[]) 
{
	if (argc < 3) {
		printf ("用法:%s <IP地址> <端口号>\n", argv[0]);
		return 0;
	}

	printf ("客户机:创建套接字...\n");
	int sockfd = socket (AF_INET, SOCK_STREAM, 0);
	if (sockfd == -1) {
		perror ("socket");
		return -1;
	}

	printf ("客户机:准备地址并连接...\n");
	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_port = htons (atoi (argv[2]));
	addr.sin_addr.s_addr = inet_addr (argv[1]);
	if (connect (sockfd, (struct sockaddr*)&addr, sizeof (addr)) == -1) {
		perror ("connect");
		return -1;
	}

	printf ("客户机:收发数据...\n");
	for (;;) {
		printf ("> ");
		char buf[1024];
		gets (buf);
		if (! strcmp (buf, "!")) break;

		if (send (sockfd, buf, strlen (buf) + 1, 0) == -1) {
			perror ("send");
			return -1;
		}

		memset (buf, 0, sizeof (buf));
		ssize_t rb = recv (sockfd, buf, sizeof (buf), 0);
		if (rb == -1) {
			perror ("recv");
			return -1;
		}
		if (rb == 0) {
			printf ("客户机:服务器已宕机!\n");
			break;
		}
		printf ("< %s\n", buf);
	}
	printf ("客户机:关闭套接字...\n");
	close (sockfd);
	return 0;
}
/*
 * tcpsvr.c
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

void sigchld (int signum) 
{
	wait (0);
	printf ("回收了一个子进程的尸体!\n");
}

int main (int argc, char* argv[]) 
{
	if (argc < 2) {
		printf ("用法:%s <端口号>\n", argv[0]);
		return -1;
	}

	if (signal (SIGCHLD, sigchld) == SIG_ERR) {
		perror ("signal");
		return -1;
	}

	printf ("服务器:创建套接字...\n");
	int sockfd = socket (AF_INET, SOCK_STREAM, 0);
	if (sockfd == -1) {
		perror ("socket");
		return -1;
	}

	printf ("服务器:准备地址并绑定...\n");
	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_port = htons (atoi (argv[1]));
	addr.sin_addr.s_addr = INADDR_ANY;
	if (bind (sockfd, (struct sockaddr*)&addr,
		sizeof (addr)) == -1) {
		perror ("bind");
		return -1;
	}

	printf ("服务器:监听套接字...\n");
	if (listen (sockfd, 1024) == -1) {
		perror ("listen");
		return -1;
	}

	for (;;) {

		printf ("服务器:等待连接请求...\n");
		struct sockaddr_in addrcli = {};
		socklen_t addrlen = sizeof (addrcli);
		int connfd = accept (sockfd, (struct sockaddr*)&addrcli, &addrlen);
		printf ("服务器:接受来自%s:%u客户机的连接请求。\n",
			inet_ntoa (addrcli.sin_addr),
			ntohs (addrcli.sin_port));

		pid_t pid = fork ();
		if (pid == -1) {
			perror ("fork");
			return -1;
		}
		if (pid == 0) {
			printf ("服务器:收发数据...\n");
			for (;;) {
				char buf[1024];
				ssize_t rb = recv (connfd, buf, sizeof (buf), 0);
				if (rb == -1) {
					perror ("recv");
					return -1;
				}
				if (rb == 0) {
					printf ("服务器:客户机已关闭连接。\n");
					break;
				}
				if (send (connfd, buf, rb, 0) == -1) {
					perror ("send");
					return -1;
				}
			}
			printf ("服务器:关闭套接字...\n");
			close (connfd);
			return 0;
		}
	}
	close (sockfd);
	return 0;
}

all:

	gcc -m32 -o cancel cancel.c -lpthread
	gcc -m32 -o create create.c -lpthread
	gcc -m32 -o detach detach.c -lpthread
	gcc -m32 -o equal  equal.c -lpthread
	gcc -m32 -o exit   exit.c -lpthread
	gcc -m32 -o join   join.c -lpthread
	gcc -m32 -o tcpcli tcpcli.c
	gcc -m32 -o tcpsvr tcpsvr.c
	gcc -m32 -o udpcli udpcli.c
	gcc -m32 -o udpsvr udpsvr.c

clean: 
	rm  -rf cancel \
		create  \
		detach  \
		equal   \
		exit    \
		join    \
		tcpcli  \
		tcpsvr  \
		udpcli  \
		udpsvr