----------2015/01/09/23:21更新----------------------------------- 关于保留DEBUG信息的一个简单例子,见这篇随笔 ----------2014/12/18/17:53更新----------------------------------- 前段时间写了这篇随笔,现在回过头来看感觉有很多问题,因此打算修正和补充一下,以前写的内容也没删,就留在这篇随笔的最下面了,可以对比着看看 目的:编写使用ZThread库的多线程程序 Windows: (Win7) 因为ZThread是支持Windows平台的,所以windows下,最好用visual studio的编译器,确保最好的兼容性 安装visual studio之后,通过开始>所有程序>Visual Studio 2013>Visual Studio Tools>VS2013 开发人员命令提示就可以使用CL、LINK、LIB、NMAKE这几个Windows下的编译和生成工具了。(相当于GNU的g++、ar、make)。 编译ZThread 从sourceforge.net下载ZThread-2.3.2.tar.gz,解压到F:/libs/ZThread-2.3.2 查看README>查看BUILDING
打开terminal,切换到目录F:/libs/ZThread-2.3.2/src 输入命令:CL /c /I..\include *.cxx 也就是编译所有的cxx文件,并生成对应的obj文件,但是不调用LINK对obj文件进行链接(比如somebody.cxx会被编译为somebody.obj) 等价于GNU的:g++ -c -I ../include *.cxx 然后输入命令:LIB /OUT:ZThread_win32.lib *.obj 也就是将所有的obj文件打包为lib文件 等价于GNU的:ar -r ZThread.a *.o 注意此处必须用CL编译的obj文件,并且由LIB打包成lib文件,如果是其他编译器编译的.a文件,你改后缀名改成.lib在windows下是用不了的,会报很多undefined reference to xxx错误。总而言之你坚持一个原则:用GNU的工具生成的库,那么引用这个库的代码就必须用GNU的编译器来编译,如果是用windows的工具生成的库,那么引用这个库的代码就必须用windows的编译器来编译!一般来说,如果一个库只使用C++ Standard里面的东西,就是平台无关的,比如ZThread就是这样的库,这样的库在几乎任何支持C++ Standard的平台上都是可以编译的,只不过,你选择了哪个平台,你最好就用哪个平台的编译器!
好,第二步,写代码,代码结构如下(加粗的就代表文件夹) zthreaddemo libs zthread ZThread_win32.lib include zthread // zthread的所有头文件 LiftOff.h Test.h src LiftOff.cpp Test.cpp main.cpp Makefile 代码我就不贴了,只贴Makefile的,你可以下载项目文件夹来看看 Makefile # See # http://msdn.microsoft.com/zh-cn/library/f35ctcxw.aspx # for more info about Microsoft Visual C++ Compiler and Linker options # Microsoft Visual C++ Compiler && Linker tool CC = cl # Microsoft Visual C++ Linker LINK = link # LIB_ZTHREAD is the ZThread static link library path # /LIBPATH:<dir> Specifies a path that the linker will search before it searches the path specified in the LIB environment option. If you want to specify more than one directory, you must specify multiple /LIBPATH options. # here we need the ZThread_win32.lib LIB_ZTHREAD = /LIBPATH:libs\zthread # tells the linker where to find object files OBJ_PATH = /LIBPATH:obj # '/I<PATH_NAME>' or '/I <PATH_NAME>' specifies a header search path # for example: CL /I \xxinclude /I\my\include main.c # tells the compiler where to find my own header files # *** This makes you free from the burden to write things like #include "../include/xxx" in your cpp files *** HEADER_PATH = /I include # See this page, search '/EHsc' # http://wenku.baidu.com/view/04a34101de80d4d8d15a4ff2.html EHSC = /EHsc # Compiles without linking. COMPILATION_ONLY = /c # Compiler output object file: /Fo<PATH_NAME>, for example: # cl /c hello.cpp /Foobj\hello.obj put the hello.obj file into the folder 'obj' C_OUT = /Fo: # Linker output executable file # note that the comma must be followed by the path WITHOUT any white-characters L_OUT = /OUT: bin\test.exe: bin obj obj\main.obj obj\Test.obj obj\LiftOff.obj $(LINK) $(LIB_ZTHREAD) $(OBJ_PATH) $(L_OUT)bin\test.exe main.obj Test.obj LiftOff.obj zthread_win32.lib obj\main.obj: src\main.cpp include\Test.h $(CC) $(EHSC) $(HEADER_PATH) $(COMPILATION_ONLY) src\main.cpp $(C_OUT)obj\main.obj obj\Test.obj: src\Test.cpp include\Test.h $(CC) $(EHSC) $(HEADER_PATH) $(COMPILATION_ONLY) src\Test.cpp $(C_OUT)obj\Test.obj obj\LiftOff.obj: src\LiftOff.cpp include\LiftOff.h $(CC) $(EHSC) $(HEADER_PATH) $(COMPILATION_ONLY) src\LiftOff.cpp $(C_OUT)obj\LiftOff.obj obj: mkdir obj bin: mkdir bin # PHONY means 'clean' is a fake target # use 'make clean' to remove all .obj files # before rebuilding # '-' means continue execute next command even if something goes wrong with this command # type 'help' to get info about 'rmdir' # type 'help rmdir' to get info about '/s' and '/q' .PHONY: clean clean: -rmdir /s /q bin -rmdir /s /q obj
这里面写了一些注释,看看可以帮助理解。总的思想就是: 先创建bin和obj文件夹,调用CL编译src里面的cpp文件,把生成的obj文件放到obj文件夹里,接着调用LINK链接所有的obj文件和lib文件,生成test.exe放到bin目录下. 要生成代码,所要做的工作就是打开VS2013 开发人员命令提示,然后切换到zthreaddemo目录中,输入命令nmake即可 里面有几个要点: 1、我将很多编译器和连接器的选项都写成了宏调用的形式,希望看起来可读性更好一些 2、注意文件的组织方式,src、include、libs、obj、bin这样的组织方式更整洁,src里面根据你的模块要求还可以设置子文件夹,include也是,libs里面根据你的需要也可以设置子文件夹,obj的组织结构一般与src是一一对应的(也就是说,如果你有src/xxx/yyy.cpp,那么生成obj文件的时候也应该有obj/xxx/yyy.obj) 3、类似于/OUT:这样的选项,冒号后面必须紧跟路径,中间不能有空格,而/I这个选项与路径之间,可以有空格也可以没空格
Linux: (Ubuntu) 编译ZThread,查看README, BUILDING 一般来说在类Unix环境下安装一个库的流程都是先./configure,再make,再make install。这样就可以 把头文件安装到/usr/local/include 把库文件(.a文件)安装到/usr/local/lib 把可执行文件安装到/usr/local/bin 但是我不知道是我的问题还是ZThread的configure代码的问题,./configure总是报错 所以我还是采取直接编译的方法,到/ZThread-2.3.2/src下运行命令g++ -I../include -fpermissive -c *.cxx 选项-fpermissive的作用是把一些error给改成warning.原因是ZThread的代码是比较陈旧的代码了,在目前的C++ Standard中过去的某些语法可能被编译器视为error,但是-fpermissive选项的意思就是告诉编译器:我确定这个代码格式没有语法或其他错误,只是比较陈旧不符合现代的标准而已。可以参考这个解释 选项-I../include是说zthread的头文件在上一级目录的include文件夹中 然后运行命令ar -r ZThread.a *.o 得到zthread.a 文件的组织方式仍然如前面所述 zthreaddemo libs zthread ZThread.a include zthread // zthread的所有头文件 LiftOff.h Test.h src LiftOff.cpp Test.cpp main.cpp Makefile 代码不变,只修改Makefile,如下所示(注意这里的Makefile与项目文件夹里的不太一样,以这里的为准) # See # http://www./software/gcc # for more info about GNU C++ Compiler and Linker options # GNU C++ Compiler && Linker tool CC = g++ # GNU C++ Linker LINK = g++ # LIB_PATH tells the linker where to find library files # -L <path> Specifies a path that the linker will search before it searches the path specified in the LIB environment option. If you want to specify more than one directory, you must specify multiple -L options. # here we need the ZThread.a LIB_PATH = -Llibs/zthread # -l<LIBRARY_NAME> specifies a library file to link # note that to use the '-l' flag, you must name # your static library file libLIBRARY_NAME.a (the prefix 'lib' # cannot be omitted) and use '-Ldir' # before you actually using the '-l' flag, where 'dir' # is the directory that your libxxx.a can be found # immediately (can't be parent directory). # You might think that it's verbose to say '-Llibs/zthread -lZThread', # why not just say 'libs/zthread/ZThread.a' ? # Yes in this case it's not a good example. But what if # you put all your library files DIRECTLY in the folder 'libs'? # Suppose you put libraryA.a libraryB.a libraryC.a DIRECTLY # in the folder 'libs', you just need to specify '-Llibs' once, # then you just say '-llibraryA' '-llibraryB' '-libraryC' to link # all three library files. LIB_FILE = -l # '-I<PATH_NAME>' specifies a header search path # for example: g++ -I /xxinclude -I /my/include main.c # tells the compiler where to find my own header files # *** This makes you free from the burden to write things like #include "../include/xxx" in your cpp files *** HEADER_PATH = -Iinclude # Compiles without linking. COMPILATION_ONLY = -c # Compiler output object file: -o <PATH_NAME/object_file.o>, for example: # g++ -c hello.cpp -o obj/hello.o put the hello.o file into the folder 'obj' C_OUT = -o # Linker output executable file L_OUT = -o bin/test: bin obj obj/main.o obj/Test.o obj/LiftOff.o $(LINK) obj/main.o obj/Test.o obj/LiftOff.o $(LIB_PATH) -lZThread -lpthread $(L_OUT) bin/test obj/main.o: src/main.cpp include/Test.h $(CC) $(HEADER_PATH) $(COMPILATION_ONLY) src/main.cpp $(C_OUT) obj/main.o obj/Test.o: src/Test.cpp include/Test.h $(CC) $(HEADER_PATH) $(COMPILATION_ONLY) src/Test.cpp $(C_OUT) obj/Test.o obj/LiftOff.o: src/LiftOff.cpp include/LiftOff.h $(CC) $(HEADER_PATH) $(COMPILATION_ONLY) src/LiftOff.cpp $(C_OUT) obj/LiftOff.o obj: mkdir obj bin: mkdir bin # PHONY means 'clean' is a fake target # use 'make clean' to remove all .obj files # before rebuilding # '-' means continue execute next command even if something goes wrong with this command # type 'rm --help' to get info about '-r' and '-f' .PHONY: clean clean: -rm -r -f bin -rm -r -f obj
--------------------------------------- 2014/12/18更新之前的内容(已陈旧,有部分误区。尽量别看这个) 用mingw32-make就行了,语法跟GNU make基本上是一样的,只是要针对windows写命令,比如linux下的rm指令(删除文件)在windows下需要换成del指令 为什么不用Cygwin?——老爱报些莫名其妙的错误。下面举个例子
下面用LIB_ZTHREAD代指Windows下的F:/libs/zthread_win32.a或者Ubuntu下的/home/admin/libs/zthread.a 用HEADER_ZTHREAD代指Windows下的F:/libs/ZThread-2.3.2/include或者Ubuntu下的/home/admin/libs/ZThread-2.3.2/include
点此下载zthread_win32.a 点此下载zthread.a
文件结构:所有的.cpp文件.h文件Makefile都在一个文件夹里,假设其目录为TEST_DIR
源代码: main.cpp 1 #include "Test.h" 2 3 using namespace std; 4 5 int main() 6 { 7 Test::testLiftOff(); 8 return 0; 9 }
Test.h 1 #ifndef TEST_H 2 #define TEST_H 3 4 5 class Test 6 { 7 public: 8 static void testLiftOff(); 9 10 private: 11 Test(); 12 ~Test(); 13 }; 14 15 #endif // TEST_H
Test.cpp 1 #include "Test.h" 2 3 #include "LiftOff.h" 4 5 #include <zthread/Thread.h> 6 7 #include <iostream> // std::cout 8 9 void Test::testLiftOff() 10 { 11 using namespace ZThread; 12 13 try { 14 for (int i = 0; i < 5; ++i) 15 { 16 Thread th(new LiftOff(10, i)); 17 } 18 std::cout << "waiting for lift off" << std::endl; 19 } catch (Synchronization_Exception &e) { 20 std::cerr << e.what() << std::endl; 21 } 22 } 23 24 Test::Test() 25 { 26 //ctor 27 } 28 29 Test::~Test() 30 { 31 //dtor 32 }
LiftOff.h 1 #ifndef LIFTOFF_H 2 #define LIFTOFF_H 3 4 #include <zthread/Runnable.h> 5 6 class LiftOff : public ZThread::Runnable 7 { 8 public: 9 LiftOff(int countDown_, int id_); 10 ~LiftOff(); 11 void run(); 12 private: 13 int countDown; 14 int id; 15 }; 16 17 #endif // LIFTOFF_H
LiftOff.cpp 1 #include "LiftOff.h" 2 3 #include <iostream> 4 5 using namespace std; 6 7 LiftOff::LiftOff(int countDown_, int id_) 8 :countDown(countDown_), id(id_) 9 { 10 // do nothing 11 } 12 13 LiftOff::~LiftOff() 14 { 15 cout << "LiftOff" << id << " destroyed" << endl; 16 } 17 18 void LiftOff::run() 19 { 20 while (countDown--) 21 cout << id << " count: " << countDown << endl; 22 cout << id << "LiftOff!" << endl; 23 }
1. Ubuntu (linux) + GNU make Makefile 1 # ZTHREAD_A the static link library file of ZThread 2 ZTHREAD_A = /home/admin/libs/zthread.a 3 # ZTHREAD_H is the directory that has all the header 4 # files of the ZThread library 5 ZTHREAD_H = /home/admin/libs/ZThread-2.3.2/include 6 7 test.exe: main.o Test.o LiftOff.o 8 g++ -o test.exe main.o Test.o LiftOff.o -s $(ZTHREAD_A) -lpthread # -lpthread is necessary to link pthread library, which is not part of the default library in Ubuntu, ZThread need pthread support 9 main.o: main.cpp 10 g++ -c main.cpp -o main.o 11 # '-I' specifies the header search directory 12 Test.o: Test.cpp Test.h 13 g++ -I $(ZTHREAD_H) -c Test.cpp -o Test.o 14 LiftOff.o: LiftOff.cpp LiftOff.h 15 g++ -I $(ZTHREAD_H) -c LiftOff.cpp -o LiftOff.o 16 17 # PHONY means 'clean' is a fake target 18 # use 'make clean' to remove all .o files 19 # before rebuilding 20 .PHONY: clean 21 clean: 22 -rm test # '-' means continue execute next command even if something goes wrong 23 -rm *.o
make clean make -f Makefile 运行成功
2. Windows + mingw32-make Makefile 1 # ZTHREAD_A the static link library file of ZThread 2 ZTHREAD_A = F:/libs/ZThread-2.3.2/lib/zthread_win32.a 3 # ZTHREAD_H is the directory that has all the header 4 # files of the ZThread library 5 ZTHREAD_H = F:/libs/ZThread-2.3.2/include 6 7 test.exe: main.o Test.o LiftOff.o 8 g++ -o test.exe main.o Test.o LiftOff.o -s $(ZTHREAD_A) 9 main.o: main.cpp 10 g++ -c main.cpp -o main.o 11 # '-I' specifies the header search directory 12 Test.o: Test.cpp Test.h 13 g++ -I $(ZTHREAD_H) -c Test.cpp -o Test.o 14 LiftOff.o: LiftOff.cpp LiftOff.h 15 g++ -I $(ZTHREAD_H) -c LiftOff.cpp -o LiftOff.o 16 17 # PHONY means 'clean' is a fake target 18 # use 'make clean' to remove all .o files 19 # before rebuilding 20 # '-' means continue execute next command even if something goes wrong with this command 21 .PHONY: clean 22 clean: 23 -del test.exe 24 -del *.o
mingw32-make clean mingw32-make -f Makefile 运行成功
3. Windows + Cygwin Makefile 1 # ZTHREAD_A the static link library file of ZThread 2 ZTHREAD_A = F:/libs/ZThread-2.3.2/lib/zthread_win32.a 3 # ZTHREAD_H is the directory that has all the header 4 # files of the ZThread library 5 ZTHREAD_H = F:/libs/ZThread-2.3.2/include 6 7 test.exe: main.o Test.o LiftOff.o 8 g++ -o test.exe main.o Test.o LiftOff.o -s $(ZTHREAD_A) 9 main.o: main.cpp 10 g++ -c main.cpp -o main.o 11 # '-I' specifies the header search directory 12 Test.o: Test.cpp Test.h 13 g++ -I $(ZTHREAD_H) -c Test.cpp -o Test.o 14 LiftOff.o: LiftOff.cpp LiftOff.h 15 g++ -I $(ZTHREAD_H) -c LiftOff.cpp -o LiftOff.o 16 17 # PHONY means 'clean' is a fake target 18 # use 'make clean' to remove all .o files 19 # before rebuilding 20 # '-' means continue execute next command even if something goes wrong with this command 21 .PHONY: clean 22 clean: 23 -rm test.exe 24 -rm *.o make clean没问题 make报错(貌似是找不到__assert的实现,真心无语),报错的详细信息见这篇随笔
|
|