【Win32多线程】使用C runtime Library

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值