如何避免使用 c runtime stdio 函数,改用Win32 Console API
如果使用MFC来开发程序,不要使用_beginthreadex()或CreateThread().若没有使用MFC,那么应该总是和多线程版本的C
Run-time Library 链接,并且总是以_beginthreadex()和_endthreadex()取代CreateThread()和ExitThread().
只要以_beginthreadex()取代CreateThread(),就可以在任何线程中安全的调用C runtime函数。
Visual C++有两个版本的 C runtime library, 一个版本给单线程使用,一个版本给多线程使用。在MFC程序中必须使用
多线程版本的C runtime library。
Q:如何线程一套适当的 C run-time library?
1.在VC的集成环境中选择
a.选【Bulid/Setting】
b.选【C/C++】
c.在【Category】列表中选择Code Generation.
d.拉下【Use run-time library】组合框
出现三个版本让你选择:
Single-Threaded(static),Multithreaded(static),Multithreaded DLL
每一个版本对应一个调试版本。当在【C/C++】选项中做了上面的更改,【Link】选项会自动的选择适当的函数库连接。
2.命令行模式更改
cl /MDd srcfile.c
或cl /ML srcfile.c
Q:如何使用_beginthreadex()和_endthreadex()?
_beginthreadex()的参数和CreateThread()的参数完全相同。不过它已经把Win32的数据类型“净化”过了。
unsigned long _beginthreadex( void *security,
unsigned stack_size,
unsigned ( __stdcall *start_address )( void * ),
void *arglist,
unsigned initflag,
unsigned *thrdaddr );
返回值传回线程的handle,此值必须被强制转换类型为Win32的HANDLE后才能使用。这个函数不是标准的ANSI C runtime
library 函数,不会在Unix或OS/2的编译器中找到这个函数。
一个简单的范例:
#include<windows.h> //因为要调用CloseHandle()
#include<process.h>
unsigned _stdcall myfunc(void * p);
void main()
{
unsigned long thd;
unsigned tid;
thd=_beginthreadex(NULL,0,myfunc,0,0,&tid);
if(thd!=NULL)
{
CloseHandle(thd);
}
}
unsigned _stdcall myfunc(void *p)
{
}
另外还有一个C runtime函数,是和ExitThread()函数相对应,为_endthreadex().
如下:
void _endthreadex(unsigned );
不要在一个以_beginthreadex()启动的线程中调用ExitThread(),因为这样C runtime library就没有机会释放为该线程而
配置的资源了。
Q:什么时候使用_beginthreadex()而非CreateThread()?
要以C runtime library写一个多线程程序,必须使用1.多线程版本的C runtime library。2.使用_beginthreadex()和
_endthreadex().
因此,一个程序如果使用多个线程,而不在任何worker线程中调用 runtime library,应该能够与单线程版的runtime
library连接并以CreateThread取代_beginthreadex()。然而,C程序不调用任何的runtime library函数是不可能的。
如果主线程以外的任何线程进行以下操作,应该使用多线程版本的C runtime library,并使用_beginthreadex()和
_endthreadex().
a.在C程序中使用malloc和free()或在C++中使用new 和delete.
b.调用stdio.h或io.h中声明的任何函数。可以使用wsprintf()格式化字符串,就不需要stdio.h
c.使用浮点变量或浮点运算函数
d.调用任何一个使用了静态缓冲区的runtime函数。如asctime(),strtok(),rand();
即如果worker线程没有使用上面那些函数,那么单线程版本的runtime library以及CreateProcess()都是安全的。
范例:worker线程搜文件中的一字符串worker线程使用fopen以及其他一些文件处理函数
/* * * Uses multiple threads to search the files * "*.c" in the current directory for the string * given on the command line. * * This example uses the multithreaded version of * the C run-time library so as to be able to use * the FILE functions as well as calloc and free. * * Build this file with the command line: cl /MD SrchCrt.c * */ #define WIN32_LEAN_AND_MEAN #include <stdio.h> #include <stdlib.h> #include <windows.h> #include <process.h> /* _beginthreadex, _endthreadex */ #include <stddef.h> #include "MtVerify.h" DWORD WINAPI SearchProc( void *arg ); #define MAX_THREADS 3 HANDLE hThreadLimitSemaphore; char szSearchFor[1024]; int main(int argc, char *argv[]) { WIN32_FIND_DATA *lpFindData; HANDLE hFindFile; HANDLE hThread; DWORD dummy; int i; if (argc != 2) { printf("Usage: %s <search-string>\n", argv[0]); return EXIT_FAILURE; } /* Put search string where everyone can see it */ strcpy(szSearchFor, argv[1]); /* Each thread will be given its own results buffer */ lpFindData = calloc( 1, sizeof(WIN32_FIND_DATA) ); /* Semaphore prevents too many threads from running */ MTVERIFY( hThreadLimitSemaphore = CreateSemaphore( NULL, /* Security */ MAX_THREADS, /* Make all of them available */ MAX_THREADS, /* No more than MAX_THREADS */ NULL ) /* Unnamed */ ); hFindFile = FindFirstFile( "*.c", lpFindData ); if (hFindFile == INVALID_HANDLE_VALUE) return EXIT_FAILURE; do { WaitForSingleObject( hThreadLimitSemaphore, INFINITE ); MTVERIFY( hThread = (HANDLE)_beginthreadex(NULL, 0, SearchProc, lpFindData, 0, &dummy ) ); MTVERIFY( CloseHandle( hThread ) ); lpFindData = calloc( 1, sizeof(WIN32_FIND_DATA) ); } while ( FindNextFile( hFindFile, lpFindData )); FindClose( hFindFile ); for (i=0; i<MAX_THREADS; i++) WaitForSingleObject( hThreadLimitSemaphore, INFINITE ); MTVERIFY( CloseHandle( hThreadLimitSemaphore ) ); return EXIT_SUCCESS; } DWORD __stdcall SearchProc( void *arg ) { WIN32_FIND_DATA *lpFindData = (WIN32_FIND_DATA *)arg; char buf[1024]; FILE* fp; fp = fopen(lpFindData->cFileName, "r"); if (!fp) return EXIT_FAILURE; while (fgets(buf, sizeof(buf), fp)) { /* Inefficient search strategy, but it's easy */ if (strstr(buf, szSearchFor)) printf("%s: %s", lpFindData->cFileName, buf); } fclose(fp); free(lpFindData); MTVERIFY( ReleaseSemaphore( hThreadLimitSemaphore, 1, // Add one to the count NULL ) ); // Do not need the old value }
---避免stdio.h
在没有stdio.h的情况下如何进行屏幕输出?
字符串格式问题,可由sprintf()的一个windows兄弟,名为wsprintf()来解决。wsprintf()分为_wsprintf()和
_wsprintfW(),前者处理ANSI字符串。后者处理Unicode字符串。这个函数是系统核心的一部分,和C runtime library没有
关系。
stdin 与stdout的替代品。
Win32之中也有完全对等与stdin,stdout,stderr的东西,可以使用API函数GetStdHandle()获取。
HANDLE GetStdHandle(
DWORD nStdHandle // input, output, or error device
);
参数nStdHandle必须是以下三个:
STD_INPUT_HANDLE Standard input handle
STD_OUTPUT_HANDLE Standard output handle
STD_ERROR_HANDLE
如何直接控制屏幕?
在Ms-Dos下可以用下面四种方法控制
a.利用BIOS显示中断服务程序
b.利用对视频显示缓冲区的直接读写
c.利用C runtime conio.h中的函数
d.利用C runtime stdio.h中的函数
应该使用Console API
使用Console API 取代stdio.h范例
#define WIN32_LEAN_AND_MEAN #include <stdio.h> #include <stdlib.h> #include <windows.h> #include <time.h> /* to init rand() */ #include "MtVerify.h" /*********************************************** * Constants */ #define MAX_THREADS 256 #define INPUT_BUF_SIZE 80 #define BANNER_SIZE 12 #define OUTPUT_TEXT_COLOR BACKGROUND_BLUE | \ FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE /********************************************** * Function Prototypes */ void MainLoop( void ); void ClearScreen( void ); void ShutDownThreads( void ); void Prompt( LPCSTR str ); /* Display title bar info */ int StripCr( LPSTR buf ); /* Thread startup function */ DWORD WINAPI BannerProc( LPVOID pParam ); /********************************************** * Global Variables */ HANDLE hConsoleIn; /* Console input */ HANDLE hConsoleOut; /* Console output */ HANDLE hRunObject; /* "Keep Running" event object */ HANDLE ThreadHandles[MAX_THREADS]; int nThreads; /* Number of threads started */ CONSOLE_SCREEN_BUFFER_INFO csbiInfo; /********************************************** * Stucture passed to thread on startup */ typedef struct { TCHAR buf[INPUT_BUF_SIZE]; SHORT x; SHORT y; } DataBlock; /********************************************** * Primary thread enters here */ int main() { /* Get display screen information & clear the screen.*/ hConsoleIn = GetStdHandle( STD_INPUT_HANDLE ); hConsoleOut = GetStdHandle( STD_OUTPUT_HANDLE ); GetConsoleScreenBufferInfo( hConsoleOut, &csbiInfo ); ClearScreen(); /* Create the event object that keeps threads running. */ MTVERIFY( hRunObject = CreateEvent( NULL, /* Security */ TRUE, /* Manual event */ 0, /* Clear on creation */ NULL) /* Name of object */ ); /* Start waiting for keyboard input to * dispatch threads or exit. */ MainLoop(); ShutDownThreads(); ClearScreen(); CloseHandle( hRunObject ); CloseHandle( hConsoleIn ); CloseHandle( hConsoleOut ); return EXIT_SUCCESS; } void ShutDownThreads( void ) { if (nThreads > 0) { /* Since this is a manual event, all * threads will be woken up at once. */ MTVERIFY( SetEvent(hRunObject) ); MTVERIFY( WaitForMultipleObjects( nThreads, ThreadHandles, TRUE, INFINITE ) != WAIT_FAILED ); while (--nThreads) MTVERIFY( CloseHandle( ThreadHandles[nThreads] ) ); } } /* Dispatch and count threads. */ void MainLoop( void ) { TCHAR buf[INPUT_BUF_SIZE]; DWORD bytesRead; DataBlock *data_block; DWORD thread_id; srand(time(NULL)); for (;;) { Prompt( "Type string to display or ENTER to exit: " ); MTVERIFY( ReadFile( hConsoleIn, buf, INPUT_BUF_SIZE-1, &bytesRead, NULL) ); /* ReadFile is binary, not line oriented, * so terminate the string. */ buf[bytesRead] = '\0'; MTVERIFY( FlushConsoleInputBuffer( hConsoleIn ) ); if (StripCr( buf ) == 0) break; if (nThreads < MAX_THREADS) { /* * Use the Win32 HeapAlloc() instead of * malloc() because we would need the * multithread library if the worker * thread had to call free(). */ data_block = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DataBlock) ); strcpy(data_block->buf, buf); /* * Pick a random place on the screen to put * this banner. You may not call rand in the * worker thread because it is one of the * functions that must maintain state * between calls. */ data_block->x = rand() * (csbiInfo.dwSize.X - BANNER_SIZE) / RAND_MAX; data_block->y = rand() * (csbiInfo.dwSize.Y - 1) / RAND_MAX + 1; MTVERIFY( ThreadHandles[nThreads++] = CreateThread( NULL, 0, BannerProc, data_block, 0, &thread_id ) ); } } } int StripCr( LPSTR buf ) { int len = strlen(buf); for (;;) { if (len <= 0) return 0; else if (buf[--len] == '\r' ) buf[len] = ' '; else if (buf[len] == '\n' ) buf[len] = ' '; else break; } return len; } void ClearScreen( void ) { DWORD dummy; COORD Home = { 0, 0 }; FillConsoleOutputAttribute( hConsoleOut, csbiInfo.wAttributes, csbiInfo.dwSize.X * csbiInfo.dwSize.Y, Home, &dummy ); FillConsoleOutputCharacter( hConsoleOut, ' ', csbiInfo.dwSize.X * csbiInfo.dwSize.Y, Home, &dummy ); } void Prompt( LPCSTR str ) { COORD Home = { 0, 0 }; DWORD dummy; int len = strlen(str); SetConsoleCursorPosition( hConsoleOut, Home ); WriteFile( hConsoleOut, str, len, &dummy, FALSE ); Home.X = len; FillConsoleOutputCharacter( hConsoleOut, ' ', csbiInfo.dwSize.X-len, Home, &dummy ); } /********************************************************** * Routines from here down are used only by worker threads */ DWORD WINAPI BannerProc( LPVOID pParam ) { DataBlock *thread_data_block = pParam; COORD TopLeft = {0,0}; COORD Size = {BANNER_SIZE ,1}; int i, j; int len; int ScrollPosition = 0; TCHAR OutputBuf[INPUT_BUF_SIZE+BANNER_SIZE]; CHAR_INFO CharBuf[INPUT_BUF_SIZE+BANNER_SIZE]; SMALL_RECT rect; rect.Left = thread_data_block->x; rect.Right = rect.Left + BANNER_SIZE; rect.Top = thread_data_block->y; rect.Bottom = rect.Top; /* Set up the string so the output routine * does not have figure out wrapping. */ strcpy(OutputBuf, thread_data_block->buf); len = strlen(OutputBuf); for (i=len; i<BANNER_SIZE; i++) OutputBuf[i] = ' '; if (len<BANNER_SIZE) len = BANNER_SIZE; strncpy(OutputBuf+len, OutputBuf, BANNER_SIZE); OutputBuf[len+BANNER_SIZE-1] = '\0'; MTVERIFY( HeapFree( GetProcessHeap(), 0, pParam ) ); do { for (i=ScrollPosition++, j=0; j<BANNER_SIZE; i++, j++) { CharBuf[j].Char.AsciiChar = OutputBuf[i]; CharBuf[j].Attributes = OUTPUT_TEXT_COLOR; } if (ScrollPosition == len) ScrollPosition = 0; MTVERIFY( WriteConsoleOutput( hConsoleOut, CharBuf, Size, TopLeft, &rect) ); /* * This next statement has the dual purpose of * being a choke on how often the banner is updated * (because the timeout forces the thread to wait for * awhile) as well as causing the thread to exit * when the event object is signaled. */ } while ( WaitForSingleObject( hRunObject, 125L ) == WAIT_TIMEOUT ); return 0; }
---结束进程
a.调用C runtime library的exit()函数
b.从main()返回系统