Linux中线程使用详解
线程与进程
为什么有了进程的概念后,还要再引入线程呢?使用多线程到底有哪些好处?什么的系统应该选用多线程?我们首先必须回答这些问题。
使用多线程的理由之一是和进程相比,它是一种非常"节俭"的多任务操作方式。我们知道,在Linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种"昂贵"的多任务工作方式。而运行于一个进程中的多个线程,它们彼此之间使用相同的地址空间,共享大部分数据,启动一个线程所花费的空间远远小于启动一个进程所花费的空间,而且,线程间彼此切换所需的时间也远远小于进程间切换所需要的时间。据统计,总的说来,一个进程的开销大约是一个线程开销的30倍左右,当然,在具体的系统上,这个数据可能会有较大的区别。
使用多线程的理由之二是线程间方便的通信机制。对不同进程来说,它们具有独立的数据空间,要进行数据的传递只能通过通信的方式进行,这种方式不仅费时,而且很不方便。线程则不然,由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便。当然,数据的共享也带来其他一些问题,有的变量不能同时被两个线程所修改,有的子程序中声明为static的数据更有可能给多线程程序带来灾难性的打击,这些正是编写多线程程序时最需要注意的地方。
除了以上所说的优点外,不和进程比较,多线程程序作为一种多任务、并发的工作方式,当然有以下的优点:
1) 提高应用程序响应。这对图形界面的程序尤其有意义,当一个操作耗时很长时,整个系统都会等待这个操作,此时程序不会响应键盘、鼠标、菜单的操作,而使用多线程技术,将耗时长的操作(time consuming)置于一个新的线程,可以避免这种尴尬的情况。
2) 使多CPU系统更加有效。操作系统会保证当线程数不大于CPU数目时,不同的线程运行于不同的CPU上。
3) 改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序会利于理解和修改。
一、线程标识
- 线程有ID, 但不是系统唯一, 而是进程环境中唯一有效.
- 线程的句柄是pthread_t类型, 该类型不能作为整数处理, 而是一个结构.
下面介绍两个函数:
- 头文件: <pthread.h>
- 原型: int pthread_equal(pthread_t tid1, pthread_t tid2);
- 返回值: 相等返回非0, 不相等返回0.
- 说明: 比较两个线程ID是否相等.
- 头文件: <pthread.h>
- 原型: pthread_t pthread_self();
- 返回值: 返回调用线程的线程ID.
二、线程创建
在执行中创建一个线程, 可以为该线程分配它需要做的工作(线程执行函数), 该线程共享进程的资源. 创建线程的函数pthread_create()
- 头文件: <pthread.h>
- 原型: int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(start_rtn)(void), void *restrict arg);
- 返回值: 成功则返回0, 否则返回错误编号.
- 参数:
- tidp: 指向新创建线程ID的变量, 作为函数的输出.
- attr: 用于定制各种不同的线程属性, NULL为默认属性(见下).
- start_rtn: 函数指针, 为线程开始执行的函数名.该函数可以返回一个void *类型的返回值,而这个返回值也可以是其他类型,并由 pthread_join()获取
- arg: 函数的唯一无类型(void)指针参数, 如要传多个参数, 可以用结构封装.
linux下多线程程序的编译方法:
因为pthread的库不是linux系统的库,所以在进行编译的时候要加上 -lpthread
# gcc filename -lpthread //默认情况下gcc使用c库,要使用额外的库要这样选择使用的库
例1:thread_create.c
#include <stdio.h>
#include <pthread.h> //包线程要包含
void *mythread1(void)
{
int i;
for(i=;i<;i++)
{
printf("this is the 1st pthread,created by zieckey.\n");
sleep();
}
}
void *mythread2(void)
{
int i;
for(i=;i<;i++)
{
printf("this is the 2st pthread,created by zieckey.\n");
sleep();
}
}
int main()
{
int ret=;
pthread_tid1,id2;
ret=pthread_create(&id1,NULL,(void*)mythread1,NULL);
if(ret)
{
printf("create pthread error!\n");
return -;
}
ret=pthread_create(&id2,NULL,(void*)mythread2,NULL);
if(ret)
{
printf("create pthread error!\n");
return -;
}
pthread_join(id1,NULL);
pthread_join(id2,NULL); return ;
}
编译步骤:gcc thread_create .c -lpthread -othread_create
例2: thread_int.c //向线程函数传递整形参数
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void *create(void *arg)
{
int *num;
num=(int *)arg;
printf("create parameter is %d \n",*num);
return (void *);
}
int main(int argc,char *argv[])
{
pthread_t tidp;
int error;
int test=;
int*attr=&test; error=pthread_create(&tidp,NULL,create,(void*)attr);
if(error)
{
printf("pthread_create is created is not created...\n");
return -;
}
sleep();
printf("pthread_create is created...\n");
return ;
}
注:字符串,结构参数,一样道理
三 线程的合并与分离
4. 线程的属性
- int pthread_attr_init(pthread_attr_t *attr);
- int pthread_attr_destory(pthread_attr_t *attr);
4.1 绑定属性
- #include <stdio.h>
- #include <pthread.h>
- ……
- int main( int argc, char *argv[] )
- {
- pthread_attr_t attr;
- pthread_t th;
- ……
- pthread_attr_init( &attr );
- pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM );
- pthread_create( &th, &attr, thread, NULL );
- ……
- }
4.2 分离属性
- pthread_attr_setdetachstat(pthread_attr_t *attr, int detachstate);
它的第二个参数有两个取值:PTHREAD_CREATE_DETACHED(分离的)和PTHREAD_CREATE_JOINABLE(可合并的,也是默认属性)。代码3演示了这个属性的使用。
- #include <stdio.h>
- #include <pthread.h>
- ……
- int main( int argc, char *argv[] )
- {
- pthread_attr_t attr;
- pthread_t th;
- ……
- pthread_attr_init( &attr );
- pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM );
- pthread_create( &th, &attr, thread, NULL );
- ……
- }
4.3 调度属性
- struct sched_param {
- int sched_priority;
- }
- int pthread_attr_setschedparam(pthread_attr_t *attr, struct sched_param *param);
- int pthread_attr_setinheritsched(pthread_attr_t *attr, int inheritsched);
- #include <stdio.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <pthread.h>
- #define THREAD_COUNT 12
- void show_thread_policy( int threadno )
- {
- int policy;
- struct sched_param param;
- pthread_getschedparam( pthread_self(), &policy, ¶m );
- switch( policy ){
- case SCHED_OTHER:
- printf( "SCHED_OTHER %d\n", threadno );
- break;
- case SCHED_RR:
- printf( "SCHDE_RR %d\n", threadno );
- break;
- case SCHED_FIFO:
- printf( "SCHED_FIFO %d\n", threadno );
- break;
- default:
- printf( "UNKNOWN\n");
- }
- }
- void* thread( void *arg )
- {
- int i, j;
- long threadno = (long)arg;
- printf( "thread %d start\n", threadno );
- sleep(1);
- show_thread_policy( threadno );
- for( i = 0; i < 10; ++i ) {
- for( j = 0; j < 100000000; ++j ){}
- printf( "thread %d\n", threadno );
- }
- printf( "thread %d exit\n", threadno );
- return NULL;
- }
- int main( int argc, char *argv[] )
- {
- long i;
- pthread_attr_t attr[THREAD_COUNT];
- pthread_t pth[THREAD_COUNT];
- struct sched_param param;
- for( i = 0; i < THREAD_COUNT; ++i )
- pthread_attr_init( &attr[i] );
- for( i = 0; i < THREAD_COUNT / 2; ++i ) {
- param.sched_priority = 10;
- pthread_attr_setschedpolicy( &attr[i], SCHED_FIFO );
- pthread_attr_setschedparam( &attr[i], ¶m );
- pthread_attr_setinheritsched( &attr[i], PTHREAD_EXPLICIT_SCHED );
- }
- for( i = THREAD_COUNT / 2; i < THREAD_COUNT; ++i ) {
- param.sched_priority = 20;
- pthread_attr_setschedpolicy( &attr[i], SCHED_FIFO );
- pthread_attr_setschedparam( &attr[i], ¶m );
- pthread_attr_setinheritsched( &attr[i], PTHREAD_EXPLICIT_SCHED );
- }
- for( i = 0; i < THREAD_COUNT; ++i )
- pthread_create( &pth[i], &attr[i], thread, (void*)i );
- for( i = 0; i < THREAD_COUNT; ++i )
- pthread_join( pth[i], NULL );
- for( i = 0; i < THREAD_COUNT; ++i )
- pthread_attr_destroy( &attr[i] );
- return 0;
- }
4.4 堆栈大小属性
- int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
4.5 满栈警戒区属性
- int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize);
- 原型: void pthread_exit(void *rval_ptr);
- 头文件: <pthread.h>
- 参数: rval_ptr是一个无类型指针, 指向线程的返回值存储变量.
pthread_join函数:
- 原型: int pthread_join(pthread_t thread, void **rval_ptr);
- 头文件: <pthread.h>
- 返回值: 成功则返回0, 否则返回错误编号.
- 参数:
- thread: 线程ID.
- rval_ptr: 指向返回值的指针(返回值也是个指针).
- 说明:
- 调用线程将一直阻塞, 直到指定的线程调用pthread_exit, 从启动例程返回或被取消.
- 如果线程从它的启动例程返回, rval_ptr包含返回码.
- 如果线程被取消, 由rval_ptr指定的内存单元置为: PTHREAD_CANCELED.
- 如果对返回值不关心, 可把rval_ptr设为NULL.
实例:
#include <pthread.h>
#include <stdio.h> /* print process and thread IDs */
void printids(const char *s)
{
pid_t pid, ppid;
pthread_t tid;
pid= getpid();
ppid = getppid();
tid = pthread_self();
printf("%16s pid %5u ppid %5u tid %16u (0x%x) ",
s, (unsigned int)pid, (unsigned int)ppid,
(unsigned int)tid, (unsigned int)tid);
}
/* thread process */
void *thread_func(void *arg);
{
printids("new thread: ");
return (void *);
}
/* main func */
int main()
{
int err;
void *tret; /* thread return value */
pthread_t ntid;
err = pthread_create(&ntid, NULL, thread_func, NULL);
if (err != )
perror("can't create thread"); err = pthread_join(ntid, &tret);
if (err != )
perror("can't join thread");
printids("main thread: ");
printf("thread exit code: %d ", (int)tret);
sleep();
return ;
}
pthread_cancel函数:
pthread_cancel函数发送终止信号
pthread_setcancelstate函数设置终止方式
pthread_testcancel函数取消线程(另一功能是:设置取消点)
1) 线程取消的定义
一般情况下,线程在其主体函数退出的时候会自动终止,但同时也可以因为接收到另一个线程发来的终止(取消)请求而强制终止。
2) 线程取消的语义
线程取消的方法是向目标线程发Cancel信号(pthread_cancel函数发送Cancel信号),但如何处理Cancel信号则由目标线程自己决定,或者忽略、或者立即终止、或者继续运行至Cancelation-point(取消点),由不同的Cancelation状态(pthread_setcancelstate函数设置状态)决定。
线程接收到CANCEL信号的缺省处理(即pthread_create()创建线程的缺省状态)是继续运行至取消点,也就是说设置一个CANCELED状态,线程继续运行,只有运行至Cancelation-point的时候才会退出。
3 )取消点
根据POSIX标准,pthread_join()、pthread_testcancel()、pthread_cond_wait()、 pthread_cond_timedwait()、sem_wait()、sigwait()等函数以及read()、write()等会引起阻塞的系统调用都是Cancelation-point,而其他pthread函数都不会引起Cancelation动作。但是pthread_cancel的手册页声称,由于LinuxThread库与C库结合得不好,因而目前C库函数都不是Cancelation-point;但CANCEL信号会使线程从阻塞的系统调用中退出,并置EINTR错误码,因此可以在需要作为Cancelation-point的系统调用前后调用 pthread_testcancel(),从而达到POSIX标准所要求的目标,即如下代码段:
pthread_testcancel();
retcode = read(fd, buffer, length);
pthread_testcancel();
4 )程序设计方面的考虑
如果线程处于无限循环中,且循环体内没有执行至取消点的必然路径,则线程无法由外部其他线程的取消请求而终止。因此在这样的循环体的必经路径上应该加入pthread_testcancel()调用。
5 )与线程取消相关的pthread函数
int pthread_cancel(pthread_t thread)
发送终止信号给thread线程,如果成功则返回0,否则为非0值。发送成功并不意味着thread会终止。
int pthread_setcancelstate(int state, int *oldstate)
设置本线程对Cancel信号的反应,state有两种值:PTHREAD_CANCEL_ENABLE(缺省)和 PTHREAD_CANCEL_DISABLE,分别表示收到信号后设为CANCLED状态和忽略CANCEL信号继续运行;old_state如果不为 NULL则存入原来的Cancel状态以便恢复。
int pthread_setcanceltype(int type, int *oldtype)
设置本线程取消动作的执行时机,type由两种取值:PTHREAD_CANCEL_DEFFERED和 PTHREAD_CANCEL_ASYCHRONOUS,仅当Cancel状态为Enable时有效,分别表示收到信号后继续运行至下一个取消点再退出和立即执行取消动作(退出);oldtype如果不为NULL则存入运来的取消动作类型值。
void pthread_testcancel(void)
功能一:设置取消点;
功能二:检查本线程是否处于Canceld状态,如果是,则进行取消动作,否则直接返回。
代码:
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h> #define THREAD_MAX 4 pthread_mutex_t mutex;
pthread_t thread[THREAD_MAX]; static int tries;
static int started; void print_it(int *arg)
{
pthread_t tid;
tid = pthread_self();
printf("Thread %lx was canceled on its %d try.\n",tid,*arg);
} void *Search_Num(int arg)
{
pthread_t tid;
int num;
int k=,h=,j;
int ntries;
tid = pthread_self(); /*while(pthread_mutex_trylock(&mutex) == EBUSY)
{
printf("**************busy****************\n");
pthread_testcancel();
}*/
srand(arg);
num = rand()&0xFFFFFF;
//pthread_mutex_unlock(&mutex); printf("thread num %lx\n",tid); ntries = ;
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,NULL); pthread_cleanup_push((void *)print_it,(void *)&ntries); while()
{
num = (num+)&0xffffff;
ntries++; if(arg == num)
{
//只允许一个线程操作此处
while(pthread_mutex_trylock(&mutex) == EBUSY) {
//一个线程操作后其余线程进入次循环挂起,等待pthread_cancel函数发送cancel信号终止线程
k++;
if(k == )
{
printf("----------2busy2-----------\n");
} pthread_testcancel();
}
tries = ntries;
//pthread_mutex_unlock(&mutex); //如果加上这句话,将会有好几个线程找到主函数中设定的值pid
printf("Thread %lx found the number!\n",tid); for(j = ;j<THREAD_MAX;j++)
{
if(thread[j]!=tid)
{
pthread_cancel(thread[j]);
}
} break;
}
if(ntries% == )
{
h++;
/*线程阻塞,其他线程争夺资源,或者是等待pthread_cancel函数发送cancel信号终止线程*/
pthread_testcancel();
/*这是为了弄明白pthread_testcancel函数的作用而设置的代码段*/
if(h == )
{
h = ;
printf("----------thread num %lx-------------\n",tid);
}
}
}
pthread_cleanup_pop();
return (void *);
} int main()
{
int i,pid; pid = getpid(); //设置要查找的数 pthread_mutex_init(&mutex,NULL);
printf("Search the num of %d\n",pid);
for(started = ; started < THREAD_MAX; started++)
{
pthread_create(&thread[started],NULL,(void *)Search_Num,(void *)pid);
} for(i = ; i < THREAD_MAX; i++)
{
printf("-----------i = %d--------------\n",i);
pthread_join(thread[i],NULL);
}
printf("It took %d tries ot find the number!\n",tries);
return ;
}
运行结果:
Search the num of
-----------i = --------------
thread num b6fbcb70
thread num b67bbb70
thread num b5fbab70
thread num b77bdb70
----------thread num b67bbb70-------------
Thread b67bbb70 found the number!
----------thread num b6fbcb70-------------
----------thread num b77bdb70-------------
----------2busy2-----------
----------thread num b5fbab70-------------
----------2busy2-----------
Thread b5fbab70 was canceled on its try.
Thread b77bdb70 was canceled on its try.
-----------i = --------------
Thread b6fbcb70 was canceled on its try.
-----------i = --------------
-----------i = --------------
It took tries ot find the number!
<span style="font-size: small;">#include <stdlib.h>
#include <stdio.h>
#include <pthread.h> void cleanup(void *arg)
{
printf("cleanup: %s\n", (char *)arg);
} void *thr_fn1(void *arg)
{
printf("thread 1 start\n");
pthread_cleanup_push(cleanup, "thread 1 first handler");
pthread_cleanup_push(cleanup, "thread 1 second handler");
printf("thread 1 push complete\n");
if (arg)
return((void *));
// pthread_exit((void *)2); pthread_cleanup_pop();
pthread_cleanup_pop();
// return((void *)1);
pthread_exit((void *)); } void *thr_fn2(void *arg)
{
printf("thread 2 start\n");
pthread_cleanup_push(cleanup, "thread 2 first handler");
pthread_cleanup_push(cleanup, "thread 2 second handler");
printf("thread 2 push complete\n");
if (arg)
pthread_exit((void *));
pthread_cleanup_pop();
pthread_cleanup_pop();
pthread_exit((void *));
} int main(void)
{
int err;
pthread_t tid1, tid2;
void *tret; err = pthread_create(&tid1, NULL, thr_fn1, (void *));
if (err != )
printf("can't create thread 1: %c\n", strerror(err));
err = pthread_create(&tid2, NULL, thr_fn2, (void *));
if (err != )
printf("can't create thread 2: %c\n", strerror(err));
err = pthread_join(tid1, &tret);
if (err != )
printf("can't join with thread 1: %c\n", strerror(err));
printf("thread 1 exit code %d\n", (int)tret);
err = pthread_join(tid2, &tret);
if (err != )
printf("can't join with thread 2: %c\n", strerror(err));
printf("thread 2 exit code %d\n", (int)tret);
exit();
}
</span>
pthread_detach()函数:
创建一个线程默认的状态是joinable。
如果一个线程结束运行但没有被join,则它的状态类似于进程中的Zombie Process,即还有一部分资源没有被回收(退出状态码).
所以创建线程者应该调用pthread_join来等待线程运行结束,并可得到线程的退出代 码,回收其资源(类似于wait,waitpid) 。
但是调用pthread_join(pthread_id)后,如果该线程没有运行结束,调用者会被阻塞,在有些情况下我们并不希望如此。
比如在Web服务器中当主线程为每个新来的链接创建一个子线程进行处理的时候,主线程并不希望因为调用pthread_join而阻塞(因为还要继续处理之后到来的链接),这时可以
1)在子线程中加入代码pthread_detach(pthread_self())
2)父线程调用pthread_detach(thread_id)(非阻塞,可立即返回)
这将该子线程的状态设置为detached,则该线程运行结束后会自动释放所有资源。
pthread_kill()函数:
pthread_kill与kill有区别,是向线程发送signal。,大部分signal的默认动作是终止进程的运行,所以,我们才要用signal()去抓信号并加上处理函数。
int pthread_kill(pthread_t thread, int sig);
向指定ID的线程发送sig信号,如果线程代码内不做处理,则按照信号默认的行为影响整个进程,也就是说,如果你给一个线程发送了SIGQUIT,但线程却没有实现signal处理函数,则整个进程退出。
pthread_kill(threadid, SIGKILL)杀死整个进程。 如果要获得正确的行为,就需要在线程内实现signal(SIGKILL,sig_handler)。所以,如果int sig的参数不是0,那一定要清楚到底要干什么,而且一定要实现线程的信号处理函数,否则,就会影响整个进程。
如果int sig是0呢,这是一个保留信号,一个作用是用来判断线程是不是还活着。pthread_kill的返回值: 成功:0 线程不存在:ESRCH 信号不合法:EINVAL
代码:
int kill_rc = pthread_kill(thread_id,0); if(kill_rc == ESRCH)
printf("the specified thread did not exists or already quit\n"); else if(kill_rc == EINVAL)
printf("signal is invalid\n");
else
printf("the specified thread is alive\n");
这里附上线程基本函数:
线程属性pthread_attr_t简介
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <pthread.h>
static void pthread_func_1 (void);
static void pthread_func_2 (void); int main (int argc, char** argv)
{
pthread_t pt_1 = ;
pthread_t pt_2 = ;
pthread_attr_t atrr = {};
int ret = ; /*初始化属性线程属性*/
pthread_attr_init (&attr);
pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM);
pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); ret = pthread_create (&pt_1, &attr, pthread_func_1, NULL);
if (ret != )
{
perror ("pthread_1_create");
} ret = pthread_create (&pt_2, NULL, pthread_func_2, NULL);
if (ret != )
{
perror ("pthread_2_create");
} pthread_join (pt_2, NULL); return ;
} static void pthread_func_1 (void)
{
int i = ; for (; i < ; i++)
{
printf ("This is pthread_1.\n"); if (i == )
{
pthread_exit ();
}
} return;
} static void pthread_func_2 (void)
{
int i = ; for (; i < ; i ++)
{
printf ("This is pthread_2.\n");
} return;
}
Linux中线程使用详解的更多相关文章
- 【转】linux中inittab文件详解
原文网址:http://www.2cto.com/os/201108/98426.html linux中inittab文件详解 init的进程号是1(ps -aux | less),从这一点就能看出, ...
- 【转】linux 中fork()函数详解
在看多线程的时候看到了这个函数,于是学习了下,下面文章写的通俗易懂,于是就开心的看完了,最后还是很愉快的算出了他最后一个问题. linux 中fork()函数详解 一.fork入门知识 一个进程,包括 ...
- Linux中的sudoer详解
目录 Linux中的sudo详解 一.引言 二.格式 三./etc/sudoers文件 四.sudoers文件讲解 五.其他 Linux中的sudo详解 一.引言 Liunx用户只有两类: 管理员用户 ...
- Linux中rm命令详解
linux下rm命令使用详解---linux删除文件或目录命令 用户可以用rm命令删除不需要的文件.该命令的功能为删除一个目录中的一个或多个文件或目录,它也可以将某个目录及其下的所有文件及子目录均删除 ...
- Linux中fork()函数详解(转载)
linux中fork()函数详解 一.fork入门知识 一个进程,包括代码.数据和分配给进程的资源.fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事, ...
- Linux中重要目录详解
Linux重要目录详解 / 根目录,第一层目录,所有其他目录的根,一般根目录下只存放目录.包括:/bin, /boot, /dev, /etc, /home, /lib, /mnt, /opt, /p ...
- 【转】linux中ifconfig 命令详解详解
1 概述 ifconfig工具不仅可以被用来简单地获取网络接口配置信息,还可以修改这些配置.用ifconfig命令配置的网卡信息,在网卡重启后机器重启后,配置就不存在.要想将上述的配置信息永远的存的电 ...
- linux中inittab文件详解
init的进程号是1(ps -aux | less),从这一点就能看出,init进程是系统所有进程的起点,Linux在完成核内引导以后,就开始运行init程序. init程序需要读取配置文件/etc/ ...
- xargs在linux中的使用详解-乾颐堂
xargs在linux中是个很有用的命令,它经常和其他命令组合起来使用,非常的灵活. xargs是给命令传递参数的一个过滤器,也是组合多个命令的一个工具.它把一个数据流分割为一些足够小的块,以方便过滤 ...
随机推荐
- python和语法糖
语法糖(syntactic sugar)是指编程语言中可以更容易的表达一个操作的语法,它可以使程序员更加容易去使用这门语言:操作可以变得更加清晰.方便,或者更加符合程序员的编程习惯.(百度百科的 ...
- hdu 1130,hdu 1131(卡特兰数,大数)
How Many Trees? Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)T ...
- Web测试中容易被忽略的Charset问题
今天继续进行一个更综合的脚本制作,录制设置.进行录制.脚本修改,一切都轻车熟路,进行得很顺利.经过近一个小时的对比和修改,OK,脚本大功告成,终于可以小试牛刀了,嘿嘿. 运行,replay lo ...
- 安装jdk8-linux版
下载jdk http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html 安装 rpm -iv ...
- apache 把404页面的url转发给php脚本处理
# .htaccess1 RewriteCond %{REQUEST_FILENAME} !-f 2 RewriteRule ^(.*)$ map.php?host=%{HTTP_HOST}& ...
- HDU 4607.Park Visit-树的直径(BFS版)+结论公式(乱推公式)-备忘(加油!)
Park Visit Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total ...
- Class文件格式
本文系<深入理解Java虚拟机>总结 ClassFile{ u4 magic; u2 minor_version; u2 major_version; u2 constant_pool_c ...
- 洛谷——P1123 取数游戏
P1123 取数游戏 题目描述 一个N×M的由非负整数构成的数字矩阵,你需要在其中取出若干个数字,使得取出的任意两个数字不相邻(若一个数字在另外一个数字相邻8个格子中的一个即认为这两个数字相邻),求取 ...
- hdu 6047 Maximum Sequence 贪心
Description Steph is extremely obsessed with “sequence problems” that are usually seen on magazines: ...
- POJ 2484 A Funny Game 博弈论 对称博弈
http://poj.org/problem?id=2484 1和2时Alice必胜,3时Bob必胜,其他情况下Bob只需要在Alice取过之后取一次将剩下的硬币链平均分为两份,然后Alice怎么取B ...