c++ 条件变量
.条件变量创建
静态创建:pthread_cond_t cond=PTHREAD_COND_INITIALIZER;
动态创建:pthread_cond _t cond;
pthread_cond_init(&cond,NULL);
其中的第二个参数NULL表示条件变量的属性,虽然POSIX中定义了条件变量的属性,但在LinuxThread中并没有实现,因此常常忽略。
.条件等待
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock(&mutex);
while(条件1)
pthread_cond_wait(&cond,&mutex);
函数操作1;
pthread_mutex_unlock(&mutex);
当条件1成立的时候,执行pthread_cond_wait(&cond,&mutex)这一句,开放互斥锁,然后线程被挂起。当条件1不成立的时候,跳过while循环体,执行函数操作1,然后开放互斥锁。
.条件激发
pthread_mutex_lock(&mutex);
函数操作2;
if(条件1不成立)
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
先执行函数操作2,改变条件状态,使得条件1不成立的时候,执行pthread_cond_signal(&cond)这句话。这句话的意思是激发条件变量cond,使得被挂起的线程被唤醒。
pthread_cond_broadcast(&cond);
这句话也是激发条件变量cond,但是,这句话是激发所有由于cond条件被挂起的线程。而signal的函数则是激发一个由于条件变量cond被挂起的线程。
.条件变量的销毁
pthread_cond_destroy(&cond);
在linux中,由于条件变量不占用任何资源,所以,这句话除了检查有没有等待条件变量cond的线程外,不做任何操作。
#include <pthread.h>
#include <stdio.h>
#include <unistd.h> pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int count = ; //pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
//pthread_mutex_lock(&mutex);
//while(条件1)
// pthread_cond_wait(&cond, &mutex);
//函数操作1
//pthread_mutex_unlock(&mutex);
//解释:当条件1成立的时候,执行pthread_cond_wait(&cond, &mutex)这一句,开放互斥锁,然后线程被挂起。
// 当条件1不成立的时候,跳过while循环体,执行函数操作1,然后开放互斥锁
// 即无论走哪个分支,都会放开互斥锁 //此线程先启动
void* decrement(void* arg) {
while() {
//条件等待
pthread_mutex_lock(&mutex);
while(count <= ) {
printf("count<=0,thread is hanging!\n");
pthread_cond_wait(&cond, &mutex);
sleep();
printf("sleep!\n");
}
count -= ;
pthread_mutex_unlock(&mutex); if (count == ) {
printf("count==9,thread1 is over!\n");
return NULL;
}
}
} //条件激发
//pthread_mutex_lock(&mutex);
//函数操作2;
//if(条件1不成立)
// pthread_cond_signal(&cond);
// pthread_mutex_unlock(&mutex);
//解释:先执行函数操作2,改变条件变量,使得条件1不成立的时候,执行pthread_cond_signal(&cond)这句话的意思是激发条件变量cond,使得被挂起的线程被唤醒。
// pthread_cond_broadcast(&cond); 激发所有由于cond条件而被挂起的线程。而signal的函数则是激发一个由于条件变量cond被挂起的线程 void *increment(void *arg) {
sleep();
while() {
pthread_mutex_lock(&mutex);
count += ;
if(count > ) {
printf("count=%d, change cond state!\n", count);
pthread_cond_signal(&cond);
}
pthread_mutex_unlock(&mutex); if (count == ) {
printf("count=10,thread is over!\n");
return NULL;
}
}
} int main(void) {
int i1=, i2=;
pthread_t id1, id2;
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cond, NULL); pthread_create(&id1, NULL, decrement, NULL);
pthread_create(&id2, NULL, increment, NULL); i2 = pthread_join(id2, NULL);
i1 = pthread_join(id1, NULL);
if ( (i2==) && (i1==) ) {
printf("count=%d, the main thread!\n", count);
pthread_cond_destroy(&cond);
pthread_mutex_destroy(&mutex);
return ;
}
return -;
}
#include <stdio.h>
#include <pthread.h>
#include <queue>
#include <string> pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
std::queue<std::string> que; void* consumer(void* arg) {
while (true) {
pthread_mutex_lock(&mutex);
while ( que.empty() ) {
printf("que is empty , thread consumer hanging!\n");
pthread_cond_wait(&cond, &mutex);
}
while (!que.empty()) {
printf("%s\n", que.front().c_str());
que.pop();
}
pthread_mutex_unlock(&mutex);
}
return NULL;
} void* producer(void* arg) {
int count = ;
while (true) {
pthread_mutex_lock(&mutex);
for (int i=; i<; i++) {
char arr[];
snprintf(arr, sizeof(arr), "my num is: %d", i);
que.push(arr);
pthread_cond_signal(&cond);
}
pthread_mutex_unlock(&mutex);
count++;
if (count > ) {
printf("stop producer %d s\n", );
sleep();
count = ;
}
}
return NULL;
} int main(void) {
pthread_t arr[];
pthread_create(&arr[], NULL, consumer, NULL);
sleep();
printf("sleep 3 s\n");
pthread_create(&arr[], NULL, producer, NULL); for(int i=; i< ; i++) {
pthread_join(arr[i], NULL);
} pthread_cond_destroy(&cond);
pthread_mutex_destroy(&mutex);
}
http://blog.csdn.net/hemmanhui/article/details/4417433
互斥锁:用来上锁。
条件变量:用来等待,当条件变量用来自动阻塞一个线程,直到某特殊情况发生为止。通常条件变量和互斥锁同时使用。
函数介绍:
1.
名称: |
pthread_cond_init |
目标: |
条件变量初始化 |
头文件: |
#include < pthread.h> |
函数原形: |
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr); |
参数: |
cptr 条件变量 attr 条件变量属性 |
返回值: |
成功返回0,出错返回错误编号。 |
pthread_cond_init函数可以用来初始化一个条件变量。他使用变量attr所指定的属性来初始化一个条件变量,如果参数attr为空,那么它将使用缺省的属性来设置所指定的条件变量。
2.
名称: |
pthread_cond_destroy |
目标: |
条件变量摧毁 |
头文件: |
#include < pthread.h> |
函数原形: |
int pthread_cond_destroy(pthread_cond_t *cond); |
参数: |
cptr 条件变量 |
返回值: |
成功返回0,出错返回错误编号。 |
pthread_cond_destroy函数可以用来摧毁所指定的条件变量,同时将会释放所给它分配的资源。调用该函数的进程也并不要求等待在参数所指定的条件变量上。
3.
名称: |
pthread_cond_wait/pthread_cond_timedwait |
目标: |
条件变量等待 |
头文件: |
#include < pthread.h> |
函数原形: |
int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex); int pthread_cond_timedwait(pthread_cond_t *cond,pthread_mutex_t mytex,const struct timespec *abstime); |
参数: |
cond 条件变量 mutex 互斥锁 |
返回值: |
成功返回0,出错返回错误编号。 |
第一个参数*cond是指向一个条件变量的指针。第二个参数*mutex则是对相关的互斥锁的指针。函数pthread_cond_timedwait函数类型与函数pthread_cond_wait,区别在于,如果达到或是超过所引用的参数*abstime,它将结束并返回错误ETIME.pthread_cond_timedwait函数的参数*abstime指向一个timespec结构。该结构如下:
typedef struct timespec{
time_t tv_sec;
long tv_nsex;
}timespec_t;
3.
名称:
pthread_cond_signal/pthread_cond_broadcast |
|
目标: |
条件变量通知 |
头文件: |
#include < pthread.h> |
函数原形: |
int pthread_cond_signal(pthread_cond_t *cond); int pthread_cond_broadcast(pthread_cond_t *cond); |
参数: |
cond 条件变量 |
返回值: |
成功返回0,出错返回错误编号。 |
参数*cond是对类型为pthread_cond_t 的一个条件变量的指针。当调用pthread_cond_signal时一个在相同条件变量上阻塞的线程将被解锁。如果同时有多个线程阻塞,则由调度策略确定接收通知的线程。如果调用pthread_cond_broadcast,则将通知阻塞在这个条件变量上的所有线程。一旦被唤醒,线程仍然会要求互斥锁。如果当前没有线程等待通知,则上面两种调用实际上成为一个空操作。如果参数*cond指向非法地址,则返回值EINVAL。
下面是一个简单的例子,我们可以从程序的运行来了解条件变量的作用。
#include <pthread.h> pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;/*初始化互斥锁*/ void *thread1(void *); int i=1; pthread_create(&t_a,NULL,thread2,(void *)NULL);/*创建进程t_a*/ void *thread1(void *junk) sleep(1); } void *thread2(void *junk) if(i%3!=0) sleep(1); } |
程序创建了2个新线程使他们同步运行,实现进程t_b打印20以内3的倍数,t_a打印其他的数,程序开始线程t_b不满足条件等待,线程t_a运行使a循环加1并打印。直到i为3的倍数时,线程t_a发送信号通知进程t_b,这时t_b满足条件,打印i值。
下面是运行结果:
#cc –lpthread –o cond cond.c
#./cond
thread1:1
thread1:2
thread2:3
thread1:4
thread1:5
thread2:6
thread1:7
thread1:8
thread2:9
备注:
pthread_cond_wait 执行的流程首先将这个mutex解锁, 然后等待条件变量被唤醒, 如果没有被唤醒, 该线程将一直休眠, 也就是说, 该线程将一直阻塞在这个pthread_cond_wait调用中, 而当此线程被唤醒时, 将自动将这个mutex加锁,然后再进行条件变量判断(原因是“惊群效应”,如果是多个线程都在等待这个条件,而同时只能有一个线程进行处理,此时就必须要再次条件判断,以使只有一个线程进入临界区处理。),如果满足,则线程继续执行,最后解锁,
也就是说pthread_cond_wait实际上可以看作是以下几个动作的合体:
解锁线程锁
等待线程唤醒,并且条件为true
加锁线程锁.
pthread_cond_signal仅仅负责唤醒正在阻塞在同一条件变量上的一个线程,如果存在多个线程,系统自动根据调度策略决定唤醒其中的一个线程,在多处理器上,该函数是可能同时唤醒多个线程,同时该函数与锁操作无关,解锁是由pthread_mutex_unlock(&mutex)完成
唤醒丢失问题
在线程并没有阻塞在条件变量上时,调用pthread_cond_signal或pthread_cond_broadcast函数可能会引起唤醒丢失问题。
唤醒丢失往往会在下面的情况下发生:
一个线程调用pthread_cond_signal或pthread_cond_broadcast函数;
另一个线程正处在测试条件变量和调用pthread_cond_wait函数之间;
没有线程正在处在阻塞等待的状态下
由来: 最近一直在想怎么高效率的在IO线程接收到数据时通知逻辑线程(基于线程池)工作的问题,像网络编程的服务器模型的一些模型都需要用到这个实现,下面我这里简单的罗列一个多线程的网络服务器模型 半同步/半异步(half-sync/half-async): 许多餐厅使用 半同步/半异步 模式的变体。例如,餐厅常常雇佣一个领班负责迎接顾客,并在餐厅繁忙时留意给顾客安排桌位,为等待就餐的顾客按序排队是必要的。领班由所有顾客“共享”,不能被任何特定顾客占用太多时间。当顾客在一张桌子入坐后,有一个侍应生专门为这张桌子服务。 对于上面罗列的这种模型,本文讨论的问题是当领班接到客人时,如何高效率的通知侍应生去服务顾客. 在我们使用很广泛的线程池实现中,也会有一样的问题 方法实现: .使用锁+轮询 使用这种方法可以很简单的实现,但是会有一定的性能消耗,其还有一个点要好好把握,就是一次轮询没有结果后相隔多久进行下一次的轮询,间隔时间太短,消耗的CPU资源较多,间隔时间太长,不能很及时的响应请求。这就相当于上面的这个例子,侍应生时不时的取询问领班有没有顾客到来 .使用条件变量的线程同步 线程条件变量pthread_cond_t 线程等待某个条件 int pthread_cond_timedwait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex,const struct timespec *restrict abstime);
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
通知函数 通知所有的线程 int pthread_cond_broadcast(pthread_cond_t *cond); 只通知一个线程
int pthread_cond_signal(pthread_cond_t *cond); 正确的使用方法 pthread_cond_wait用法: pthread_mutex_lock(&mutex); while(condition_is_false) { pthread_cond_wait(&cond,&mutex); } condition_is_false=true; //此操作是带锁的,也就是说只有一个线程同时进入这块 pthread_mutex_unlock(&mutex); pthread_cond_signal用法: pthread_mutex_lock(&mutex); condition_is_false=false; pthread_cond_signal(&cond) pthread_mutex_unlock(&mutex) 我刚初用的时候,觉得非常的奇怪,为什么要这样用,加了mutex后还需要一个condition_is_false变量来表示有没有活干。其实这样子的一个操作主要是为了解决“假激活”问题,因为我么您这里的使用场景,只需要激活一个线程,因为一个线程干一个活,而不是多个线程干一个活,所以为了避免线程被激活了,但实际又没有事情干,所以使用了这么一套机制。 实际上,信号和pthread_cond_broadcast是两个常见的导致假唤醒的情况。假如条件变量上有多个线程在等待,pthread_cond_broadcast会唤醒所有的等待线程,而pthread_cond_signal只会唤醒其中一个等待线程。这样,pthread_cond_broadcast的情况也许要在pthread_cond_wait前使用while循环来检查条件变量。 来个例子:
复制代码 #include <pthread.h>
#include <stdio.h>
#include<stdlib.h>
#include<unistd.h> /* For safe condition variable usage, must use a boolean predicate and */
/* a mutex with the condition. */
int workToDo = ;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; #define NTHREADS 20 static void checkResults(char *string, int rc) {
if (rc) {
printf("Error on : %s, rc=%d",
string, rc);
exit(EXIT_FAILURE);
}
return;
} void *threadfunc(void *parm)
{
int rc; while () {
/* Usually worker threads will loop on these operations */
rc = pthread_mutex_lock(&mutex);
checkResults("pthread_mutex_lock()\n", rc); while (!workToDo) {
printf("Thread blocked\n");
rc = pthread_cond_wait(&cond, &mutex);
checkResults("pthread_cond_wait()\n", rc);
}
printf("Thread awake, finish work!\n");
sleep();
/* Under protection of the lock, complete or remove the work */
/* from whatever worker queue we have. Here it is simply a flag */
workToDo = ;
printf("In mutex lock\n");
rc = pthread_mutex_unlock(&mutex);
sleep();
printf("Out mutex lock\n");
checkResults("pthread_mutex_lock()\n", rc);
}
return NULL;
} int main(int argc, char **argv)
{
int rc=;
int i;
pthread_t threadid[NTHREADS]; printf("Enter Testcase - %s\n", argv[]); printf("Create %d threads\n", NTHREADS);
for(i=; i<NTHREADS; ++i) {
rc = pthread_create(&threadid[i], NULL, threadfunc, NULL);
checkResults("pthread_create()\n", rc);
} sleep(); /* Sleep is not a very robust way to serialize threads */ for(i=; i<; ++i) {
printf("Wake up a worker, work to do...\n"); rc = pthread_mutex_lock(&mutex);
checkResults("pthread_mutex_lock()\n", rc); /* In the real world, all the threads might be busy, and */
/* we would add work to a queue instead of simply using a flag */
/* In that case the boolean predicate might be some boolean */
/* statement like: if (the-queue-contains-work) */
if (workToDo) {
printf("Work already present, likely threads are busy\n");
}
workToDo = ;
rc = pthread_cond_broadcast(&cond);
// rc = pthread_cond_signal(&cond);
checkResults("pthread_cond_broadcast()\n", rc); rc = pthread_mutex_unlock(&mutex);
checkResults("pthread_mutex_unlock()\n", rc);
sleep(); /* Sleep is not a very robust way to serialize threads */
} printf("Main completed\n");
exit();
return ;
} 复制代码 事实上上面的例子无论是使用pthread_cond_signal还是pthread_cond_broadcast,都只会打印Thread awake, finish work5次,大家可能会觉得非常奇怪,但是实际情况就是这样的。 为了明白其pthread_cont_wait内部干了什么工作,有必要深入一下其内部实现。 关于其内部实现伪代码如下:
复制代码 pthread_cond_wait(mutex, cond):
value = cond->value; /* 1 */
pthread_mutex_unlock(mutex); /* 2 */
pthread_mutex_lock(cond->mutex); /* 10 */ pthread_cond_t自带一个mutex来互斥对waiter等待链表的操作
if (value == cond->value) { /* 11 */ 检查一次是不是cond有被其他线程设置过,相当于单例模式的第二次检测是否为NULL
me->next_cond = cond->waiter;
cond->waiter = me;//链表操作
pthread_mutex_unlock(cond->mutex);
unable_to_run(me);
} else
pthread_mutex_unlock(cond->mutex); /* 12 */
pthread_mutex_lock(mutex); /* 13 */ pthread_cond_signal(cond):
pthread_mutex_lock(cond->mutex); /* 3 */
cond->value++; /* 4 */
if (cond->waiter) { /* 5 */
sleeper = cond->waiter; /* 6 */
cond->waiter = sleeper->next_cond; /* 7 */ //链表操作
able_to_run(sleeper); /* 8 */ 运行sleep的线程,即上面的me
}
pthread_mutex_unlock(cond->mutex); /* 9 */ 复制代码 pthread_cond_broadcast虽然能够激活所有的线程,但是激活之后会有mutex锁,也就是说他的激活是顺序进行的,只有第一个激活的线程调用pthread_mutex_unlock(&mutex)后,后一个等待的线程才会继续运行.因为从pthread_cond_wait(&cond,&mutex)到pthread_mutex_unlock(&mutex)区间是加的独占锁,从wait激活后的第一个线程占用了这个锁,所以其他的线程不能运行,只能等待。所以当第一个被激活的线程修改了condition_is_false后(上面测试代码的workToDo),接着调用pthread_mutex_unlock(&mutex)后,此时其他等待在cond的一个线程会激活,但是此时condition_is_false已经被设置,所以他跑不出while循环,当调用pthread_cond_wait时,其内部pthread_mutex_unlock(mutex)调用会导致另一个在它后面的等待在cond的线程被激活。 所以,通过这种方式,即便是误调用了pthread_cond_broadcast或者由于信号中断的原因激活了所有在等待条件的线程,也能保证其结果是正确的。 另外说一句题外话,很多人写的基于条件变量线程同步的框架,说自己是无锁的,其实这是不对的,只是内部锁的机制在pthread_cond_wait实现了而已,其还是基于互斥锁的实现。真正想要达到无锁的可以关注一下lockfree相关的CAS算法,其内部使用一个intel CPU的cmpxchg8指令完成的,其实这种实现个人认为和传统锁相比只是一个非阻塞锁和阻塞锁的区别。
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> // for safe condition variable usage, must use a boolean predicate and
// a mutex with the condition int work_to_do = ;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; #define NUM_THREADS 20 static void check_result(char *str, int rc) {
if (rc) {
printf("Error on: %s, rc=%d", str, rc);
exit(EXIT_FAILURE);
}
return;
} void *threadfunc(void *param) {
int rc;
while () {
//usually woker threads will loop on these operations
rc = pthread_mutex_lock(&mutex);
check_result("pthread_mutex_lock()\n", rc); while (!work_to_do) {
printf("thread hanging\n");
rc = pthread_cond_wait(&cond, &mutex);
check_result("pthread_cond_wait()\n", rc);
}
printf("thread awake, finish work!\n");
sleep();
// under protection of the lock, complete or remove the work
// from whatever worker queue we have, here it is simply a flag
work_to_do = ;
printf("in mutex lock\n");
rc = pthread_mutex_unlock(&mutex);
sleep();
printf("out mutex lock\n");
check_result("pthread_mutex_unlock()\n", rc);
}
return NULL;
} int main(void) {
int rc = ;
int i;
pthread_t threadid[NUM_THREADS]; printf("Enter %d threads\n", NUM_THREADS);
for (i=; i<NUM_THREADS; ++i) {
rc = pthread_create(&threadid[i], NULL, threadfunc, NULL);
check_result("pthread_create()\n", rc);
} sleep(); //sleep is not a very robust way to serialize threads for(i=; i<; ++i) {
printf("wake up a worker, work to do...\n"); rc = pthread_mutex_lock(&mutex);
check_result("pthread_mutex_lock()\n", rc); // in the real world, all the threads might be busy, and
// we would add work to a queue instead of simply using a flag
// in that case the boolean predicate might be some boolean
// statement like: if (the-queue-contains-work) if(work_to_do) {
printf("work already present, likely threads are busy\n");
}
work_to_do = ;
//rc = pthread_cond_broadcast(&cond);
rc = pthread_cond_signal(&cond);
check_result("pthread_cond_broadcast()\n", rc); rc = pthread_mutex_unlock(&mutex);
check_result("pthread_mutex_unlock()\n", rc);
sleep(); // sleep is not a very robust way to serialize threads
} printf("mian complete\n");
exit();
return ;
}
c++ 条件变量的更多相关文章
- Boost条件变量condition_variable_any
Boost条件变量可以用来实现线程同步,它必须与互斥量配合使用.使用条件变量实现生产者消费者的简单例子如下,需要注意的是cond_put.wait(lock)是在等待条件满足.如果条件不满足,则释放锁 ...
- Linux线程同步:条件变量
条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足,它常和互斥锁一起使用.使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥锁并等待条件发生变化.一旦其它 ...
- C++多线程の条件变量
如果有一个队列,方法一和方法二:方法一是生产者,方法二是消费者: 两者不停的相互等待,加减锁,为了减少不必要的等待,我们可以使用条件变量, 条件的变量的第二个参数的加入可以控制多个线程的"消 ...
- Linux 多线程条件变量同步
条件变量是线程同步的另一种方式,实际上,条件变量是信号量的底层实现,这也就意味着,使用条件变量可以拥有更大的自由度,同时也就需要更加小心的进行同步操作.条件变量使用的条件本身是需要使用互斥量进行保护的 ...
- linux线程同步(2)-条件变量
一.概述 上一篇,介绍了互斥量.条件变量与互斥量不同,互斥量是防止多线程同时访问共享的互斥变量来保 ...
- 第8章 用户模式下的线程同步(4)_条件变量(Condition Variable)
8.6 条件变量(Condition Variables)——可利用临界区或SRWLock锁来实现 8.6.1 条件变量的使用 (1)条件变量机制就是为了简化 “生产者-消费者”问题而设计的一种线程同 ...
- 条件变量pthread_cond_t怎么用
#include <pthread.h> #include <stdio.h> #include <stdlib.h> pthread_mutex_t mutex ...
- 转载~kxcfzyk:Linux C语言多线程库Pthread中条件变量的的正确用法逐步详解
Linux C语言多线程库Pthread中条件变量的的正确用法逐步详解 多线程c语言linuxsemaphore条件变量 (本文的读者定位是了解Pthread常用多线程API和Pthread互斥锁 ...
- [转]一个简单的Linux多线程例子 带你洞悉互斥量 信号量 条件变量编程
一个简单的Linux多线程例子 带你洞悉互斥量 信号量 条件变量编程 希望此文能给初学多线程编程的朋友带来帮助,也希望牛人多多指出错误. 另外感谢以下链接的作者给予,给我的学习带来了很大帮助 http ...
- 在SQL存储过程中给条件变量加上单引号
在SQL存储过程中给条件变量加上单引号,不加语句就会出问题,以下就是在存储过程中将条件where设置成了动态变化的,给where赋完值再和前面的语句拼接,再execute(SQL) ), )), )+ ...
随机推荐
- npm install出现"Unexpected end of JSON input while parsing near"
打开命令行输入 npm cache clean --force 重新npm i,即可解决报错
- LOJ #10084. 「一本通 3.3 练习 1」最小圈(二分+SPFA判负环)
题意描述: 见原LOJ:https://loj.ac/problem/10084 题解: 假设所求的平均最小值为X,环上各个边的权值分别为A1,A2...Ak,可以得到: X=(A1+A2+A3+.. ...
- js加密php解密---jsencrypt
原理:javascript加密PHP解密: 完全依赖openssl: 一. openssl 是干嘛的 它集成了众多密码算法及实用工具 rsa加密流程:(今天只讲众多加密方式中的一种) 1. 在当前文件 ...
- 生成缓存文件cache file
生成缓存文件cache file class Test{ public function index(){ $arrConfig = Array( 'name' => 'daicr', 'age ...
- helm-chart-2-chart结构和简单模板
1, chart 结构介绍 我们创建一个chart 并查看其结构 右侧注释为其文件的的解释 $ helm create mychart $ cd mychart/ $ tree ├── charts ...
- 100本Python精品书籍(附pdf电子书下载)
51本Python精品书籍(附下载)链接: https://pan.baidu.com/s/19ydAKCFxM0plkepXMlqQLg 提取码: nnpe 400集python视频教程下载:链接: ...
- wriesharek同时监听多个端口
之前的文章<wireshark解析自定义的protobuf协议> ,当时只监听了一个端口,而如果游戏同时有二个 socket 连接,比如一个是网关另外一个是其它的,怎么办呢? for i, ...
- CentOS安装mariadb做为mysql的替代品
mariadb做为mysql的替代品 现在centos的新版本yum包已换成mariadb 安装一些库 yum install gcc gcc-c++ wget net-tools 复制代码 查看SE ...
- grid - 隐式命名网格线名称
1.隐式的指定网格线反向指定了隐式的网格区域名称,命名的网格区域隐式的命名了网格线名称. 指定网格区域会给网格区域边线添加隐式的网格线名称.这些网格线的命名是基于网格区域来命名,只是在网格区域名称的后 ...
- 通过配置JVM参数解决生成日志存在乱码问题
项目上生产环境时出现一个问题,生成的日志文件存在乱码,服务器编码为 LANG=zh_US.UTF-8,按理说中文不会存在乱码,这里存在一个问题,就是在SIT等其他环境我们使用的连接工具为Xshell, ...