mikan's technical note

仕事&趣味で実験した小技の備忘録です(Linux,windows,DOS等)

MENU

【C言語】マルチスレッドの排他処理

Win32API
CreateEvent()
SetEvent
CreateThread()
WaitForSingleObject()
WaitForMultipleObjects
を使用したマルチスレッド排他処理の動作確認です。

//
// 【スレッドで順次処理(順不同)】※Windows7で動作確認済
// (c) 2017 mikan
// ※使用にあたっては利用者の自己責任でお願いします。
// ※参考文献 Advaned Windows 改訂第4版 Jeffrey Richter
//

#include "stdafx.h"
#include "windows.h"

#define   MAX_RUN_THREAD_NUM    32        // 作成するスレッドの数

int       gCount;                         // カウンタ
int       gNumber[MAX_RUN_THREAD_NUM];    // スレッド番号
char      gName[256];                     // スレッド名
HANDLE    ghEvent;                        // イベントハンドル

// ---------- 状態表示 ----------
void print_status()
{
    int     i;

    printf("%s:", gName);
    for (i = 0; i < MAX_RUN_THREAD_NUM; i++) {
        printf("%2d,", gNumber[i]);
    }
    printf("count=%2d\n", gCount);
}

// ---------- スレッド ----------
int Thread(PVOID pvParam)
{
    int     *n;

    // イベントシグナル待ち(30秒でタイムアウト)
    DWORD dw = WaitForSingleObject(ghEvent, 30 * 1000);
    if (dw == WAIT_FAILED || dw == WAIT_TIMEOUT) {
        return -1;
    }

    n = (int*)(pvParam);
    gNumber[gCount] = *n;
    gCount++;
    sprintf(gName, "Thread%02d", *n);

    // 状態表示
    print_status();

    //Sleep(200);    // debug用

    // イベントシグナル
    SetEvent(ghEvent);

    return 0;
}

// ---------- 親スレッド ----------
int ThreadCtrl()
{
    int     x[MAX_RUN_THREAD_NUM];
    int     i;
    DWORD   dwThreadId[MAX_RUN_THREAD_NUM];
    HANDLE  hThread[MAX_RUN_THREAD_NUM];

    // 初期化
    gCount = 0;
    memset(gNumber, 0x00, sizeof(gNumber));
    memset(gName, 0x00, sizeof(gName));
    memset(hThread, 0x00, sizeof(hThread));
    for (i = 0; i < MAX_RUN_THREAD_NUM; i++) {
        x[i] = i+1;
    }

    // 自動リセットイベント作成(いずれかのスレッド1つだけが、シグナルを検出)
    ghEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

    // 手動リセットイベント作成(全てのスレッドが、同時にシグナルを検出)
    // ghEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

    // スレッド作成
    for(i=0; i<MAX_RUN_THREAD_NUM; i++) {
        hThread[i] = CreateThread(
                         NULL,
                         0,
                         (PTHREAD_START_ROUTINE)Thread,
                         (PVOID)&x[i],
                         0,
                         &dwThreadId[i]);
    }

    // イベントシグナル
    SetEvent(ghEvent);

    // 全スレッド終了待ち
    DWORD dw = WaitForMultipleObjects(
                   MAX_RUN_THREAD_NUM,
                   hThread,
                   TRUE,
                   50 * 1000); // 50秒でタイムアウト

    // スレッドハンドルクローズ
    for (i = 0; i < MAX_RUN_THREAD_NUM; i++) {
        CloseHandle(hThread[i]);
    }

    if (dw == WAIT_FAILED || dw == WAIT_TIMEOUT) {
        return -1;
    }

    return 0;
}

// ---------- メイン ----------
void main()
{
    int     x;
    HANDLE  hThreadCtrl;
    DWORD   dwThreadCtrlId;
    DWORD   dwStart;
    
    dwStart = GetTickCount();

    printf("開始\n");

    // 親スレッド作成
    hThreadCtrl = CreateThread(
                      NULL,
                      0,
                      (PTHREAD_START_ROUTINE)ThreadCtrl,
                      (PVOID)&x,
                      0,
                      &dwThreadCtrlId);

    // 親スレッドの終了待ち(60秒でタイムアウト)
    DWORD dw = WaitForSingleObject(hThreadCtrl, 60 * 1000);

    // 親スレッドハンドルクローズ
    CloseHandle(hThreadCtrl);

    printf("終了\n");
    printf("経過時間=%d ms\n", GetTickCount() - dwStart);

    if (dw == WAIT_FAILED || dw == WAIT_TIMEOUT) {
        ;
    }

    return;
}

【実行結果】
※本PGでは各スレッドの起動順序はOSまかせとなります
f:id:myerss555:20170709051413j:plain

【手動リセットイベントで全スレッドを同時実行した結果】
※カウンタを正しくインクリメント出来ない可能性あり
f:id:myerss555:20170709052211j:plain

【C言語】VisualStudio 2015 VC++ 「strcpy()」 コンパイルエラー回避方法

ちょっとお試しでコーディングしたところこんなエラーが出ました。

C4996
'strcpy':
This function or variable may be unsafe.
Consider using strcpy_s instead.
To disable deprecation, use _CRT_SECURE_NO_WARNINGS.

strcpyは使うなってことみたいですけどそうも言ってられないので対応策を。
(最終的な実行環境は WindowsNT4.0/Windows95, Visual C++ 4.0 なので・・・)

stdafx.h に1行追加することでコンパイルが通ります。
----------------------------------------
#pragma once

// ↓この1行を追加
#define _CRT_SECURE_NO_WARNINGS

#include "targetver.h"

#include <stdio.h>
#include <tchar.h>
----------------------------------------

【C言語】printfで実行した行番号を出力する

//
// 【printfで実行した行番号を出力する】※Solaris,RedHutで動作確認済
// (c) 2017 mikan
// ※使用にあたっては利用者の自己責任でお願いします。
//
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>

// printfマクロ
#define DBGPRI(fmt, ...) debug_printf(__FILE__, __func__, __LINE__, fmt, ##__VA_ARGS__)

// printf関数
void debug_printf(const char *file, const char *func, const int line, const char *fmt, ...)
{
    printf("%s,%s(),%d,%d:", file, func, line, errno);

    va_list ap;
    va_start(ap, fmt);
    vfprintf(stdout, fmt, ap);
    va_end(ap);

    printf("\n");
}

int main()
{
  DBGPRI("start");              // Sun C 5.8 ... compile Warning, gcc ... compile OK
  DBGPRI("start %s", "");       // compile OK
  DBGPRI("start %s", "test");   // compile OK

  return 0;
}

【実行結果】
dbgpri.c,main(),23,0:start
dbgpri.c,main(),24,0:start
dbgpri.c,main(),25,0:start test