try{ // 可能抛出异常 throw
}catch(){ //捕获异常
}finally{ 不管有没有抛出异常或者异常有没有被捕获,都会走到这里 return除外。
}
在c中,goto是不能跨越函数的,而执行这种类型跳转功能的是setjmp 和 longjmp
#include <setjmp.h>
int setjmp(jmp_buf env); 直接调用返回0,若从longjmp调用返回longjmp第二个参数
void longjmp(jmp_buf env , int val);
jmp_buf是某种形式的数组,其中存放在调用longjmp时能用来恢复栈状态的所有信息。因为需要在另一个函数种引用env变量,所以通常将env变量定义为全局变量。
使用longjmp第二个参数的原因是:对于一个setjmp可以有多个longjmp。
#include <setjmp.h>
#include <stdio.h>
jmp_buf env;
int count = 0;
void sub_func(int idx) {
printf("sub_func --> idx:%d\n", idx);
longjmp(env, idx);
}
int main(int argc, char *argv[]) {
int idx = 0;
count = setjmp(env);
if (count == 0) {
printf("count:%d\n", count);
sub_func(++idx);
} else if (count == 1) {
printf("count:%d\n", count);
sub_func(++idx);
} else if (count == 2) {
printf("count:%d\n", count);
sub_func(++idx);
} else if (count == 3) {
printf("count:%d\n", count);
sub_func(++idx);
} else {
printf("other count\n");
}
return 0;
}
longjmp跳转之后函数栈会被覆盖,不会造成栈溢出。
setjmp、longjmp和try-carth的关系
try ---> setjmp 相当于设置这个标签
throw ----> longjmp(5) 跳转到setjmp
catch(5) ------> 抛出的是什么就捕获什么
一个进程中线程直接除了线程自己的栈和寄存器之外,其他几乎都是共享的,如果线程想维护一个只属于线程自己的全局变量怎么办?线程的私有存储解决了这个问题。
下面说一下线程存储的具体用法。
1. 创建一个类型为 pthread_key_t 类型的变量。
2. 调用 pthread_key_create() 来创建该变量。该函数有两个参数,第一个参数就是上面声明的 pthread_key_t 变量,第二个参数是一个清理函数,用来在线程释放该线程存储的时候被调用。该函数指针可以设成 NULL ,这样系统将调用默认的清理函数。
3. 当线程中需要存储特殊值的时候,可以调用 pthread_setspecific() 。该函数有两个参数,第一个为前面声明的 pthread_key_t 变量,第二个为 void* 变量,这样你可以存储任何类型的值。
4. 如果需要取出所存储的值,调用 pthread_getspecific() 。该函数的参数为前面提到的 pthread_key_t 变量,该函数返回 void * 类型的值。
pthread_key_t无论是哪一个线程创建,其他所有的线程都是可见的,即一个进程中只需phread_key_create()一次。看似是全局变量,然而全局的只是key值,对于不同的线程对应的value值是不同的(通过pthread_setspcific()和pthread_getspecific()设置)。
int pthread_key_create(pthread_key_t *key, void (*destr_function) (void *))不论哪个线程调用pthread_key_create(),所创建的key都是所有线程可访问的,但各个线程可根据自己的需要往key中填入不同的值,这就相当于提供了一个同名而不同值的全局变量。
int pthread_setspecific(pthread_key_t key, const void *pointer) void * pthread_getspecific(pthread_key_t key)写入(pthread_setspecific())时,将pointer的值(不是所指的内容)与key相关联,而相应的读出函数则将与key相关联的数据读出来。数据类型都设为void *,因此可以指向任何类型的数据。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#define PTHREAD_COUNT 2
pthread_key_t key;
typedef void* (*thread_cb)(void*);
struct pair {
int x;
int y;
};
void *thread1_proc(void *arg) {
struct pair p = {1, 2};
int i = 0;
while (++i < 100) {
p.x ++;
p.y ++;
pthread_setspecific(key, &p);
struct pair *res = (struct pair *)pthread_getspecific(key);
printf("x: %d, y: %d\n", res->x, res->y);
}
}
void *thread2_proc(void *arg) {
int i = 0;
while (++i < 100) {
pthread_setspecific(key, &i);
int *res = (int *)pthread_getspecific(key);
printf("res: %d\n", *res);
}
}
void *thread3_proc(void *arg) {
int i = 0;
while (++i < 100) {
struct pair *res = (struct pair *)pthread_getspecific(key);
printf("x: %d, y: %d\n", res->x, res->y);
}
}
int main(int argc, char *argv[]) {
pthread_key_create(&key, NULL);
pthread_t thread_id[PTHREAD_COUNT] = {0};
thread_cb thread_proc[PTHREAD_COUNT] = {
thread1_proc,
thread2_proc
};
int i = 0;
for (i = 0;i < PTHREAD_COUNT;i ++) {
pthread_create(&thread_id[i], NULL, thread_proc[i], NULL);
}
for (i = 0;i < PTHREAD_COUNT;i ++) {
pthread_join(thread_id[i], NULL);
}
pthread_key_delete(key);
}
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <pthread.h>
#include <setjmp.h>
#define ntyThreadData pthread_key_t
#define ntyThreadDataSet(key, value) pthread_setspecific((key), (value))
#define ntyThreadDataGet(key) pthread_getspecific((key))
#define ntyThreadDataCreate(key) pthread_key_create(&(key), NULL)
#define EXCEPTIN_MESSAGE_LENGTH 512
typedef struct _ntyException {
const char *name;
} ntyException;
ntyException SQLException = {"SQLException"};
ntyException TimeoutException = {"TimeoutException"};
ntyThreadData ExceptionStack;
typedef struct _ntyExceptionFrame {
jmp_buf env;
int line;
const char *func;
const char *file;
ntyException *exception;
struct _ntyExceptionFrame *prev;
char message[EXCEPTIN_MESSAGE_LENGTH+1];
} ntyExceptionFrame;
#define ntyExceptionPopStack \
ntyThreadDataSet(ExceptionStack, ((ntyExceptionFrame*)ntyThreadDataGet(ExceptionStack))->prev)
#define ReThrow ntyExceptionThrow(frame.exception, frame.func, frame.file, frame.line, NULL)
#define Throw(e, cause, ...) ntyExceptionThrow(&(e), __func__, __FILE__, __LINE__, cause, ##__VA_ARGS__, NULL)
enum {
ExceptionEntered = 0,
ExceptionThrown,
ExceptionHandled,
ExceptionFinalized
};
#define Try do { \
volatile int Exception_flag; \
ntyExceptionFrame frame; \
frame.message[0] = 0; \
frame.prev = (ntyExceptionFrame*)ntyThreadDataGet(ExceptionStack); \
ntyThreadDataSet(ExceptionStack, &frame); \
Exception_flag = setjmp(frame.env); \
if (Exception_flag == ExceptionEntered) {
#define Catch(e) \
if (Exception_flag == ExceptionEntered) ntyExceptionPopStack; \
} else if (frame.exception == &(e)) { \
Exception_flag = ExceptionHandled;
#define Finally \
if (Exception_flag == ExceptionEntered) ntyExceptionPopStack; \
} { \
if (Exception_flag == ExceptionEntered) \
Exception_flag = ExceptionFinalized;
#define EndTry \
if (Exception_flag == ExceptionEntered) ntyExceptionPopStack; \
} if (Exception_flag == ExceptionThrown) ReThrow; \
} while (0)
static pthread_once_t once_control = PTHREAD_ONCE_INIT;
static void init_once(void) {
ntyThreadDataCreate(ExceptionStack);
}
void ntyExceptionInit(void) {
pthread_once(&once_control, init_once);
}
void ntyExceptionThrow(ntyException *excep, const char *func, const char *file, int line, const char *cause, ...) {
va_list ap;
ntyExceptionFrame *frame = (ntyExceptionFrame*)ntyThreadDataGet(ExceptionStack);
if (frame) {
frame->exception = excep;
frame->func = func;
frame->file = file;
frame->line = line;
if (cause) {
va_start(ap, cause);
vsnprintf(frame->message, EXCEPTIN_MESSAGE_LENGTH, cause, ap);
va_end(ap);
}
ntyExceptionPopStack;
longjmp(frame->env, ExceptionThrown);
} else if (cause) {
char message[EXCEPTIN_MESSAGE_LENGTH+1];
va_start(ap, cause);
vsnprintf(message, EXCEPTIN_MESSAGE_LENGTH, cause, ap);
va_end(ap);
printf("%s: %s\n raised in %s at %s:%d\n", excep->name, message, func ? func : "?", file ? file : "?", line);
} else {
printf("%s: %p\n raised in %s at %s:%d\n", excep->name, excep, func ? func : "?", file ? file : "?", line);
}
}
/* ** **** ******** **************** debug **************** ******** **** ** */
ntyException A = {"AException"};
ntyException B = {"BException"};
ntyException C = {"CException"};
ntyException D = {"DException"};
void *thread(void *args) {
pthread_t selfid = pthread_self();
Try {
Throw(A, "A");
} Catch (A) {
printf("catch A : %ld\n", selfid);
} EndTry;
Try {
Throw(B, "B");
} Catch (B) {
printf("catch B : %ld\n", selfid);
} EndTry;
Try {
Throw(C, "C");
} Catch (C) {
printf("catch C : %ld\n", selfid);
} EndTry;
Try {
Throw(D, "D");
} Catch (D) {
printf("catch D : %ld\n", selfid);
} EndTry;
Try {
Throw(A, "A Again");
Throw(B, "B Again");
Throw(C, "C Again");
Throw(D, "D Again");
} Catch (A) {
printf("catch A again : %ld\n", selfid);
} Catch (B) {
printf("catch B again : %ld\n", selfid);
} Catch (C) {
printf("catch C again : %ld\n", selfid);
} Catch (D) {
printf("catch B again : %ld\n", selfid);
} EndTry;
}
#define THREADS 50
int main(void) {
ntyExceptionInit();
Throw(D, NULL);
Throw(C, "null C");
printf("\n\n=> Test1: Try-Catch\n");
Try {
Try {
Throw(B, "recall B");
} Catch (B) {
printf("recall B \n");
} EndTry;
Throw(A, NULL);
} Catch(A) {
printf("\tResult: Ok\n");
} EndTry;
printf("=> Test1: Ok\n\n");
printf("=> Test2: Test Thread-safeness\n");
#if 1
int i = 0;
pthread_t threads[THREADS];
for (i = 0;i < THREADS;i ++) {
pthread_create(&threads[i], NULL, thread, NULL);
}
for (i = 0;i < THREADS;i ++) {
pthread_join(threads[i], NULL);
}
#endif
printf("=> Test2: Ok\n\n");
}