3
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

[C++] タイマー、それもコンソール・アプリケーションで別スレッド立てて (C#プログラマ向け)

Last updated at Posted at 2017-02-28

囲碁プログラムを作ろうと思うんだが、
C#プログラマーには タイマー1個使うにも C++ はよくわからん。
作るのは コマンドライン打ち込みができる コンソール・アプリケーションだ。

あと、Unityなら初期装備のユニット・テストも Visual Studio 2015 C++ では なんか導入方法が分からんかった。困ったもんだ。

とりあえず タイマーを自作してみた。
関係ないコードを削って 抜粋してみる。削りそこなうかもしれないが……。

1段階目

Console_Kifuwarabe.cpp

// Console_Kifuwarabe.cpp : Defines the entry point for the console application.
//

# include "stdafx.h"
# include <iostream>
# include "UtilFile.h"
# include "UtilTimer.h"

/**
 * 指定時間間隔で実行したい処理を書くクラス
 */
class Worker : public DefaultWorker
{
public:
	Worker() : DefaultWorker()
	{
		std::cout << "サブクラスのコンストラクター、呼んでるのかだぜ☆(^~^)?" << std::endl;
	}

	void OnStep()
	{
		std::cout << "別スレッドで実行中だぜ☆(^▽^)v" << std::endl;
		// TODO: ここにやりたい処理を書く
	}
};

int main()
{
	// スレッドテスト
	UtilTimer timer;
	Worker* pWorker = new Worker();
	// スレッドスタート。
	timer.Start(pWorker,1000);
	// 10秒経ったら スレッドを止めよう☆(^~^)
	UtilTimer::Sleep_Milliseconds(5000L);
	std::cout << "5秒経ったけど、動いてんの☆(^~^)?" << std::endl;
	UtilTimer::Sleep_Milliseconds(5000L);
	timer.Break_Force();
	delete pWorker;

	// 横軸はアルファベットにする☆(1桁で済む) Iを飛ばすのは 少なくともわたしはグニュー碁1.2(1995年)では見かけた昔からある習慣☆ 縦棒と区別するぜ☆(^▽^)
	std::cout	<< "  |ABCDEFGHJKLMNOPQRST|" << std::endl
				<< "--+-------------------+" << std::endl
				<< " 1|                   |" << std::endl
				<< " 2|                   |" << std::endl
				<< " 3|                   |" << std::endl
				<< " 4|                   |" << std::endl
				<< " 5|                   |" << std::endl
				<< " 6|                   |" << std::endl
				<< " 7|                   |" << std::endl
				<< " 8|                   |" << std::endl
				<< " 9|                   |" << std::endl
				<< "10|                   |" << std::endl
				<< "11|                   |" << std::endl
				<< "12|                   |" << std::endl
				<< "13|                   |" << std::endl
				<< "14|                   |" << std::endl
				<< "15|                   |" << std::endl
				<< "16|                   |" << std::endl
				<< "17|                   |" << std::endl
				<< "18|                   |" << std::endl
				<< "19|                   |" << std::endl
				<< "--+-------------------+" << std::endl
				<< "何かボタンを押せだぜ☆(^▽^)" << std::endl;
	getchar();
	//system("pause");

    return 0;
}

UtilTimer.h

# pragma once
# include <iostream>
# include <fstream>
# include <atomic>
# include <thread>
# include <chrono>

/**
* 指定時間間隔で実行したい処理を書くクラス
*/
class DefaultWorker
{
public:
	DefaultWorker()
	{
		std::cout << "スーパー・クラスのコンストラクターはこれだけど……☆(^~^)" << std::endl;
	}

	virtual void OnStep()
	{
		std::cout << "このOnStep()は実行してはいけないぜ☆(>_<)" << std::endl;
		// なんにもしないぜ☆(^▽^)
	}

	/**
	 * ファンクターというのは、クラス名の後ろに() 演算子を付けたものです。
	 * ファンクターの引数は 無しにしておいてください。
	 */
	virtual void operator()()
	{
		std::cout << "スーパークラスのファンクターなんか呼んでんの☆(^~^)?" << std::endl;
		// なんにもしないぜ☆(^▽^)
	}
};

/**
 * スレッドを立て、テキストファイルの内容が更新されていないか確認します。
 * 参照 : 「c++で一定間隔で関数を実行させたい」 (teratail) https://teratail.com/questions/27449
 * 参照 : 「std::thread::thread」 (C++言語の入門サイトです) http://kaworu.jpn.org/cpp/std::thread::thread
 * 参照 : 「std::this thread::sleep for」 (C++言語の入門サイトです) http://kaworu.jpn.org/cpp/std::this_thread::sleep_for
 * 参照 : 「C++1z ラムダ式での*thisのコピーキャプチャ」 (Faith and Brave - C++で遊ぼう) http://faithandbrave.hateblo.jp/entry/2016/04/27/191209
 * 参照 : 「C++ で継承したときにサブクラスのメンバ関数を呼ぶためには virtual をつけて仮想関数にする」 (sotarokのお勉強) http://d.hatena.ne.jp/strkpy/20100401/1270133069
 */
class UtilTimer
{
public:
	UtilTimer();
	~UtilTimer();

	/**
	 * スレッドを開始します。
	 */
	void Start(DefaultWorker* pWorker, long milliseconds);
	/**
	 * スタートさせたスレッドが動いている間は、処理を先に進ませません。
	 */
	void Block();
	/**
	 * 処理の途中でも、強制的にスレッドを終了します。
	 */
	void Break_Force();

	/**
	 * (このクラスの持つスレッドではなく)この関数を呼び出したスレッドを、指定ミリ秒停止させます。
	 * ネームスペースを切るのがめんどくさいので このクラスに置いている。
	 */
	static void Sleep_Milliseconds(long milliseconds);

protected:
	std::thread m_thread1;
	/**
	 * スレッドを停止しても、処理のループは動き続けているようだったので、
	 * このフラグを偽にすることで、スレッドの中のループから抜けさせます
	 */
	bool m_alive;
	/**
	 * 別スレッドで実行したい処理を書いたメソッドを持っています。
	 */
	DefaultWorker* m_pWorker;
};

UtilTimer.cpp

# include "stdafx.h"
# include "UtilTimer.h"

UtilTimer::UtilTimer()
{
}


UtilTimer::~UtilTimer()
{
}

void UtilTimer::Start(DefaultWorker* pWorker, long milliseconds)
{
	m_alive = true;
	m_pWorker = pWorker;
	m_thread1 = std::thread([=,&milliseconds] {
		// 処理と処理の間を n 秒以上空ける。(毎回 n 秒間隔保障なので、n 秒毎に定期的にスタートされるという意味ではない)
		const std::chrono::milliseconds interval(milliseconds);
		while (this->m_alive) {
			
			auto start = std::chrono::system_clock::now();

			// 処理が書かれたメソッドを呼び出す
			this->m_pWorker->OnStep();

			auto end = std::chrono::system_clock::now();
			auto elapsed = end - start;
			std::cout << "elapsed.count()=[" << elapsed.count() << "]" << std::endl;

			// 毎回、インターバル時間のスリープを保障
			if (elapsed < interval) {
				std::this_thread::sleep_for(interval - elapsed);
			}
		}

		std::cout << "finished." << std::endl;
	});
}

void UtilTimer::Block()
{
	// スタートさせたスレッドが終了するまで、これ以上進まない。
	m_thread1.join();
	// スレッドを止めるか、勝手に止まるかすると ここから以下に進む。					  
}

void UtilTimer::Break_Force()
{
	// スレッドを止める
	m_thread1.detach();

	// スレッドの中のループから抜けさせる
	this->m_alive = false;
}

void UtilTimer::Sleep_Milliseconds(long milliseconds)
{
	std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
}

とりあえず 動いている……、よし!

2段階目

コメントをもろた。

UtilTimer.h

DefaultWorkerクラスの OnStep() メソッドんとこを次のように書き換えた。すっきりした。

/**
 * 指定時間間隔で実行したい処理を書くクラス
 */
class DefaultWorker
{
public:
	DefaultWorker()
	{
		std::cout << "スーパー・クラスのコンストラクターはこれだけど……☆(^~^)" << std::endl;
	}

	virtual void OnStep() = 0;
};

別スレッドでテキスト・ファイルを読込もう

別記事 : 「[C++] テキストファイル読書き (C#プログラマ向け)」 http://qiita.com/muzudho1/items/fa1446b85ae64d01a5ca

参照 : 「std::string の前後の余分な空白文字を取り除く」 (C++ プログラミング) http://program.station.ez-net.jp/special/handbook/cpp/string/trim.asp

これと組み合わせて、別スレッドで テキスト・ファイルを監視して 存在したら読込むようにしよう。

Worker クラスを、 ReaderWorker に改造する。

Console_Kifuwarabe.cpp

/**
 * 指定時間間隔で実行したい処理を書くクラス
 *
 * 参照 : 「ファイルやディレクトリの存在確認を行う方法」 (教えて!goo) https://oshiete.goo.ne.jp/qa/3399080.html
 * 参照 : 「FAQ: Cannot convert from 'const char [..]' to 'LPCTSTR'」 (Visual Studio) https://social.msdn.microsoft.com/Forums/vstudio/en-US/c1b08c0a-a803-41c3-ac8c-84eba3be1ddb/faq-cannot-convert-from-const-char-to-lpctstr?forum=vclanguage
 */
class ReaderWorker : public DefaultWorker
{
public:
	ReaderWorker() : DefaultWorker()
	{
		std::cout << "サブクラスのコンストラクター、呼んでるのかだぜ☆(^~^)?" << std::endl;
	}

	void OnStep()
	{
		std::string contents;
		if (UtilFile::Read(Kifuwarabe::filename_move, contents)) {
			contents = UtilString::Trim(contents);
			std::cout << "指し手は[" << contents << "]だぜ☆(^▽^)v" << std::endl;

		}
		else {
			std::cout << "ファイル[" << Kifuwarabe::filename_move << "]開けね☆(^~^)" << std::endl;
		}
	}
};

ほんとはここに 局面パーサーとか 書いて 思考ルーチンを叩きたい。

テストは ほとんど同じ。 main関数で試し書き。

	// スレッドテスト
	UtilTimer timer;
	ReaderWorker* pWorker1 = new ReaderWorker();
	// スレッドスタート。
	timer.Start(pWorker1, 1000);
	// 10秒経ったら スレッドを止めよう☆(^~^)
	UtilTimer::Sleep_Milliseconds(5000L);
	std::cout << "5秒経ったけど、動いてんの☆(^~^)?" << std::endl;
	UtilTimer::Sleep_Milliseconds(5000L);
	timer.Break_Force();
	delete pWorker1;

本番では スレッドを止めずに 結果が出るまで ずっと動かしておけばいいだろう。

3
5
6

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?