C/C++:多線程下使用dlopen、dlsym、dlclose裝載動态庫
當在多線程下dlopen同一個動态庫,使用的會是同一個動态庫執行個體還是不同的動态庫執行個體呢?
count.h
#ifndef _COUNT_H
#define _COUNT_H
#include <pthread.h>
int count;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int get();
void inc();
#endif
count.c
#include "count.h"
int get()
{
return count;
}
void inc()
{
pthread_mutex_lock(&mutex);
count++;
pthread_mutex_unlock(&mutex);
}
main.c
#include <stdio.h>
#include <dlfcn.h>
#include <pthread.h>
#define NUM 1000
#define LIBPATH "/home/test1280/libcount.so"
static const char *threadA = "Thread-A";
static const char *threadB = "Thread-B";
void *ThreadRun(void *arg)
{
usleep(*);
printf("Start:%s\n", (char *)arg);
void *handler = dlopen(LIBPATH, RTLD_LAZY);
if (handler == NULL)
{
printf("ERROR:%s:dlopen\n", dlerror());
}
void (*inc)() = (void (*)())dlsym(handler, "inc");
if (inc == NULL)
{
printf("ERROR:%s:dlsym\n", dlerror());
}
int i = ;
for (; i < NUM; i++)
{
inc();
}
int (*get)() = (int (*)())dlsym(handler, "get");
if (get == NULL)
{
printf("ERROR:%s:dlsym\n", dlerror());
}
printf("INFO:%s:get() return %d\n", (char *)arg, get());
dlclose(handler);
}
int main()
{
pthread_t tid1;
pthread_t tid2;
pthread_create(&tid1, NULL, ThreadRun, (void *)threadA);
printf("create Thread-A OK!!!\n");
pthread_create(&tid2, NULL, ThreadRun, (void *)threadB);
printf("create Thread-B OK!!!\n");
while ();
return ;
}
[test128@localhost ~]$ gcc -o main main.c -ldl -lpthread
[test128@localhost ~]$ ./main
create Thread-A OK!!!
create Thread-B OK!!!
Start:Thread-A
INFO:Thread-A:get() return
Start:Thread-B
INFO:Thread-B:get() return
^C
這麼看來同一程序實體不同線程dlopen相同的庫會得到不同的執行個體?
一個動态庫會在同一程序同一時刻同時被加載多次?
當然不對啦!
那按照我們期待的,希望輸出的最後的值應該是2000,那為何是1000呢?
原因:
線程A或者線程B跑的太快了,在一個時間片内就已經做完:
1.裝載動态庫到目前程序;
2.獲得函數位址;
3.調用inc指定次數;
4.輸出get傳回值;
5.dlclose解除安裝動态庫;
注意:
dlclose時,此時此刻可能僅僅隻有一個線程在跑着,另一個線程還沒有dlopen那個動态庫,此時隻有一個線程持有libcount.so的句柄,然後做完所有操作,dlclose,發現對庫的引用減少至0,就将libcount.so從目前程序解除安裝;
然後呢,就是另一個線程在跑,再打開dlopen同一個動态庫,然後裡面的全局變量(庫内全局)靜态初始化為0,然後inc,然後…
結果很明顯:是由于某個線程跑的太快,導緻另一個線程還沒有執行dlopen時,前一個程序已經做完所有活了(包括解除安裝libcount.so庫),當另一個線程執行dlopen時,完全不知道這個庫曾經被加載到目前程序…于是庫中全局變量又靜态初始化…
這樣的時序看來,輸出兩個1000合情合理.
如何驗證我們剛剛說的時序呢?
修改下main.c:
#include <stdio.h>
#include <dlfcn.h>
#include <pthread.h>
#define NUM 1000
#define LIBPATH "/home/test1280/libcount.so"
static const char *threadA = "Thread-A";
static const char *threadB = "Thread-B";
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void *ThreadRun(void *arg)
{
usleep(*);
printf("Start:%s\n", (char *)arg);
void *handler = dlopen(LIBPATH, RTLD_LAZY);
if (handler == NULL)
{
printf("ERROR:%s:dlopen\n", dlerror());
}
void (*inc)() = (void (*)())dlsym(handler, "inc");
if (inc == NULL)
{
printf("ERROR:%s:dlsym\n", dlerror());
}
int i = ;
for (; i < NUM; i++)
{
inc();
}
int (*get)() = (int (*)())dlsym(handler, "get");
if (get == NULL)
{
printf("ERROR:%s:dlsym\n", dlerror());
}
printf("INFO:%s:get() return %d\n", (char *)arg, get());
pthread_mutex_lock(&mutex);
dlclose(handler);
printf("INFO:lib unload && %s END...\n", (char *)arg);
pthread_mutex_unlock(&mutex);
}
int main()
{
pthread_t tid1;
pthread_t tid2;
pthread_create(&tid1, NULL, ThreadRun, (void *)threadA);
printf("create Thread-A OK!!!\n");
pthread_create(&tid2, NULL, ThreadRun, (void *)threadB);
printf("create Thread-B OK!!!\n");
while ();
return ;
}
[test128@localhost ~]$ gcc -o main main.c -ldl -lpthread
[test128@localhost ~]$ ./main
create Thread-A OK!!!
create Thread-B OK!!!
Start:Thread-A
INFO:Thread-A:get() return
INFO:lib unload && Thread-A END...
Start:Thread-B
INFO:Thread-B:get() return
INFO:lib unload && Thread-B END...
^C
另外,我想看看兩個線程對同一個庫執行個體進行操作是什麼效果…?
我們隻要讓兩個線程同時(同一時刻)對一個庫保持打開狀态即可(不dlclose):
将ThreadRun中最後的dlclose删除,如下main.c所示:
void *ThreadRun(void *arg)
{
usleep(*1000);
printf("Start:%s\n", (char *)arg);
void *handler = dlopen(LIBPATH, RTLD_LAZY);
if (handler == NULL)
{
printf("ERROR:%s:dlopen\n", dlerror());
}
void (*inc)() = (void (*)())dlsym(handler, "inc");
if (inc == NULL)
{
printf("ERROR:%s:dlsym\n", dlerror());
}
int i = ;
for (; i < NUM; i++)
{
inc();
}
int (*get)() = (int (*)())dlsym(handler, "get");
if (get == NULL)
{
printf("ERROR:%s:dlsym\n", dlerror());
}
printf("INFO:%s:get() return %d\n", (char *)arg, get());
//delete...
//dlclose(handler);
}
[test128@localhost ~]$ ./main
create Thread-A OK!!!
create Thread-B OK!!!
Start:Thread-A
INFO:Thread-A:get() return
Start:Thread-B
INFO:Thread-B:get() return
^C
當然,這個示例并不好,有dlopen,就應該有配套的dlclose,雖然如此,但是可以很好地驗證我們的設想.
結論:
同一程序同一時刻,不同線程通過dlopen打開相同的動态庫,隻是把一份動态庫裝載到目前程序,程序中僅僅有一個動态庫執行個體,動态庫中全局變量對各個線程來說是“同一份”.
同一程序不同時刻,不同線程雖然dlopen了,但是可能由于時序問題以及dlclose解除安裝動态庫,存在多次加載動态庫的情況,那麼也就是從程序的整體生命周期來看是不同的庫執行個體了,庫中的全局變量對于不同的線程,在不同的時間段來說是“不相同的”.
同一時刻,一個程序内僅僅有同一個動态庫執行個體哦.