如何避免使用 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()返回系统