Home > Archives > 2008-07

2008-07

Python: コマンドライン引数の取得 - sys.argv変数

コマンドラインで与える引数によってプログラムの挙動を変えたいという場面はよくあります。Python ではコマンドライン引数は sys モジュールの argv 属性に文字列を要素とするリストとして格納されています。そして、リストの先頭要素(sys.argv[0])はスクリプトファイル名となっています。

ソースコード

# coding: Shift_JIS

import sys # モジュール属性 argv を取得するため

argvs = sys.argv  # コマンドライン引数を格納したリストの取得
argc = len(argvs) # 引数の個数
# デバッグプリント
print argvs
print argc
print
if (argc != 2):   # 引数が足りない場合は、その旨を表示
    print 'Usage: # python %s filename' % argvs[0]
    quit()         # プログラムの終了

print 'The content of %s ...n' % argvs[1]
f = open(argvs[1])
line = f.readline() # 1行読み込む(改行文字も含まれる)
while line:
    print line
    line = f.readline()
f.close

text.txt

It is meaningless only to think my long further aims idly.
It is important to set my aims but at the same time I should confirm my present condition.
Unless I set the standard where I am in any level, I'll be puzzled about what I should do from now on.

実行結果

引数を指定しない場合

$ python argv01.py
['argv01.py']
1

Usage: # python SCRIPTNAME.py filename

引数を指定した場合

$ python argv01.py text.txt
['argv01.py', 'text.txt']
2

The content of text.txt ...

It is meaningless only to think my long further aims idly.

It is important to set my aims but at the same time I should confirm my present condition.

Unless I set the standard where I am in any level, I'll be puzzled about what I should do from now on.

リファレンス

C++, boost::thread : スレッドの同期と排他制御 - mutex、conditionクラス

複数のスレッドから1つの変数にアクセスする際、システム側のスレッドスケジューリング次第で、予期せぬ書き換えが起こってしまう場合があります。その為、ある1つのスレッドが変数にアクセスしている際は他のスレッドをブロックする排他制御やスレッドの同期を行う必要があります。C++でJavaのsynchronizedメソッド/ブロックと同じような記法でクリティカルセクションを実装する方法の1つにboost::threadライブラリのmutexとconditionクラスがあります。

mutex クラスの使い方

スレッドの排他制御を実現できます。具体的な使い方は、mutexインスタンスをmutex::scoped_lockクラスのコンストラクタの引数に指定し、そのインスタンスを取得することでロックをかけられます。あるスレッドが上の処理を以ってmutexインスタンスにロックをかけた場合、その他のスレッドは再度同一のmutexインスタンスにロックをかけられないようになっており、その他のスレッドはscoped_lockのコンストラクタ途中で待たされます。

mutexインスタンスのロック解除は、ロックをかけたスレッドがscoped_lockインスタンスのデストラクタを実行することで完了します。

condition クラスの使い方

次に、複数のスレッドを同期させる処理について。例えば、あるスレッドが排他的にある変数にアクセスしようとするが、その前にif文を用いて「Wait! そのアクセスちょっと待った!しばらく他のスレッドの処理を待て!」みたいな処理を行いたい場合。そのスレッドは既にmutex変数でロックを取得していますが、「待ち」に入る際はロックを解除する必要があります。

上述のような処理を実現するのがconditionクラスです。メンバ関数のwait()に引数にmutexインスタンスを指定することで、そのスレッドはロックを解除し一時停止します。他のスレッドがnotify_all()を実行すると、一時停止中の全てのスレッドが実行可能状態になります。これによって、スレッドの同期を実現します。

実際にどんな挙動か確かめよう

上の説明は以下のサンプルと実行結果を先に確認した後の方がしっくりくるかもしれません。

ソースコード

#include <iostream>
#include <string>
#include <boost/thread.hpp>
#include <boost/bind.hpp>
using namespace std;
using namespace boost;

class HandleData {
 public:
  HandleData()
    : index_(0), iarr_len_(sizeof(iarr_)/sizeof(iarr_[0])), num_data_(0){}

  // データの追加
  void putData(int n) {
    // mutexインスタンスにロックをかける
    mutex::scoped_lock look(thread_sync_);
    while (index_ >= iarr_len_) {
      printf("[putData] waitn");
      // 引数のmutexインスタンスのロックを解除する。
      // notify_all()などが呼ばれるまでこのスレッドを一時停止する
      thread_state_.wait(thread_sync_);
    }
    printf("put: %d in iarr_[%d]n", n, index_);
    iarr_[index_++] = n;
    int loop = 1000000;
    while(loop--) ; // 空回し用
    thread_state_.notify_all();
  } // lockのデストラクタが呼ばれてロック解除(lookインスタンスのスコープ切れ)

  // データの取得
  int getData() {
    // putData()と同じくiarr_にスレッドセーフでアクセスする為にロックをかける
    mutex::scoped_lock look(thread_sync_);
    while (index_ <= 0) { // putData側のthread_state_.notify_all();が実行されるまで待つ
      printf("[getData] waitn");
      thread_state_.wait(thread_sync_);
    }
    num_data_ = iarr_[--index_];
    printf("get: %d in iarr_[%d]n", num_data_, index_);
    thread_state_.notify_all();
    return num_data_;
  } // lookのデストラクタでロック解除

 private:
  mutex thread_sync_;
  condition thread_state_;
  volatile int index_;
  int iarr_[100]; // putData()、getData()からアクセスされる変数
  const unsigned int iarr_len_;
  int num_data_;
};

void threadPut(HandleData *hd) {
  const unsigned int NUM_LOOP = 100;
  for (int i = 0; i < NUM_LOOP; i++) {
    hd->putData(i);
  }
}

void threadGet(HandleData *hd) {
  const unsigned int NUM_LOOP = 100;
  for (int i = 0; i < NUM_LOOP; i++) {
    hd->getData();
  }
}

int main()
{
  HandleData hd;
  thread thr_put(bind(&threadPut, &hd));
  thread thr_get(bind(&threadGet, &hd));
  thr_put.join();
  thr_get.join();
  return 0;
}

実行結果の一例

実行する環境によって、出力結果は変化します(格納・出力順序など)。

put: 0 in iarr_[0]
get: 0 in iarr_[0]
put: 1 in iarr_[0]
get: 1 in iarr_[0]
<中略>
get: 12 in iarr_[1]
get: 11 in iarr_[0]
[getData] wait
put: 13 in iarr_[0]
put: 14 in iarr_[1]
get: 14 in iarr_[1]
get: 13 in iarr_[0]
[getData] wait
put: 15 in iarr_[0]
put: 16 in iarr_[1]
<中略>
get: 95 in iarr_[0]
[getData] wait
put: 97 in iarr_[0]
put: 98 in iarr_[1]
get: 98 in iarr_[1]
get: 97 in iarr_[0]
[getData] wait
put: 99 in iarr_[0]
get: 99 in iarr_[0]

volatile 修飾子の必要性

52行目のメンバ変数のindexの宣言

volatile int index_;

volatile(揮発性)接頭語がつくと、コンパイラによる最適化を防ぐことができます。これによって、プログラムはindex_を読み取る際は必ずメモリから読みにいきます。これは変数が複数スレッドから常に書き換えられても、必ずそれが反映されるということです。

このサンプルでは付けなくても最適化によるバグの発生はなさそうかな...?

リファレンス

C++, boost::thread : スレッドグループの生成と実行

同じような処理を行うスレッドが複数ある場合は、それらをスレッドグループでまとめると、スレッドへの操作がやり易くなります。スレッドグループへの登録には、boost::thread ライブラリの thread_group クラスを用いて、メンバ関数 create_thread() の引数にマルチスレッドで実行したい関数のアドレスを指定します。

それでは下記のサンプルで実際にその過程と実行結果を確認してみましょう。

ソースコード

#include <iostream>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
using namespace std;
using namespace boost;

// マルチスレッドで実行する関数
void run(const char thr_name) {
  int count = 0;
  while(1) {
    if (++count % 100000000 == 0) // 空回り用の if 文
      cout << "Thread-" << thr_name << ": No." << count << endl;
  }
}

int main()
{
  const char chs[] = {'A', 'B', 'C', 'D'}; // 生成スレッドの名前の配列
  const int NUM_THREAD = sizeof(chs) / sizeof(chs[0]); // 配列の要素数の算出

  // スレッドグループの生成と実行開始
  thread_group thr_grp;
  for (int i = 0; i < NUM_THREAD; ++i) {
    thr_grp.create_thread(bind(&run, chs[i])); // create_thread()でrun()を別スレッドで実行
  }
  // join_all()で全スレッドの終了を待つ
  thr_grp.join_all();
  return 0;
}

実行結果の一例

実行する環境によって、出力結果は変化します(順序など)。

Thread-B: No.100000000
Thread-A: No.100000000
Thread-C: No.100000000
Thread-D: No.100000000
Thread-B: No.200000000
Thread-A: No.200000000
Thread-C: No.200000000
Thread-D: No.200000000
Thread-B: No.300000000
Thread-A: No.300000000
Thread-C: No.300000000
Thread-D: No.300000000
Thread-B: No.400000000
Thread-A: No.400000000
<略>

リファレンス

Python: 正規表現の基本 - 文字範囲の指定「[ ]」

ソースコード

# coding: Shift_JIS

import re # 正規表現を扱うモジュールのインポート

# 正規表現のチェックプリント用の関数
def PrintRegMatch(pat, txt):
    # 探索される文字列をテキスト
    # 探索する  文字列をパターン
    # 書式: re.match(パターン, テキスト)
    m = re.match(pat, txt) # パターンにマッチしなかった場合はNoneを返す
    if m != None: print 'パターン"%s"はテキスト"%s"にマッチ「する」' % (pat, txt)
    else: print 'パターン"%s"はテキスト"%s"にマッチ「しない」' % (pat, txt)

# []で括られた文字の内どれか1つにマッチすれば真
PrintRegMatch('[ABC]D', 'CD') # A or B or C の次に D がくるパターンにマッチ
PrintRegMatch('[A-C]E', 'CE') # [A-C]は[ABC]と同意
print

# [A-Z]は全てのアルファベット大文字の内のどれか1つの意
PrintRegMatch('[A-Z]*END', 'CHSHSBSRAWRGARENDGREW')
PrintRegMatch('A[A-Z]*E', 'AgafgafE')
PrintRegMatch('A[a-z]*E', 'AewnglfngowE')

# [0-9]は0~9の数字の内どれか1つの意
PrintRegMatch('[0-9]*-[0-9]*-[0-9]*', '03-3333-2222')
print

# []内の「^」否定
PrintRegMatch('[^ABC]D', 'DD') # A or B or C でない文字の次に D がくるパターンにマッチ
PrintRegMatch('[^A-Z]*-[^A-Z]*', '164-9999')
print

PrintRegMatch('[ABC][DEF]', 'CE')
PrintRegMatch('[ABC][DEF]', 'CC')
PrintRegMatch('[a-zA-Z]*',    'FjAVzxRUqyOIn')
PrintRegMatch('[a-z][A-Z]*',  'FjAVzxRUqyOIn')
PrintRegMatch('[a-z]*[A-Z]*', 'FjAVzxRUqyOIn')

実行結果

パターン"[ABC]D"はテキスト"CD"にマッチ「する」
パターン"[A-C]E"はテキスト"CE"にマッチ「する」

パターン"[A-Z]*END"はテキスト"CHSHSBSRAWRGARENDGREW"にマッチ「する」
パターン"A[A-Z]*E"はテキスト"AgafgafE"にマッチ「しない」
パターン"A[a-z]*E"はテキスト"AewnglfngowE"にマッチ「する」
パターン"[0-9]*-[0-9]*-[0-9]*"はテキスト"03-3333-2222"にマッチ「する」

パターン"[^ABC]D"はテキスト"DD"にマッチ「する」
パターン"[^A-Z]*-[^A-Z]*"はテキスト"164-9999"にマッチ「する」

パターン"[ABC][DEF]"はテキスト"CE"にマッチ「する」
パターン"[ABC][DEF]"はテキスト"CC"にマッチ「しない」
パターン"[a-zA-Z]*"はテキスト"FjAVzxRUqyOIn"にマッチ「する」
パターン"[a-z][A-Z]*"はテキスト"FjAVzxRUqyOIn"にマッチ「しない」
パターン"[a-z]*[A-Z]*"はテキスト"FjAVzxRUqyOIn"にマッチ「する」

リファレンス

Python: 正規表現の基本 - 繰り返し「*」「+」「?」

ソースコード

# coding: Shift_JIS

import re # 正規表現を扱うモジュールのインポート

# 正規表現のチェックプリント用の関数
def PrintRegMatch(pat, txt):
    #   書式: re.match(パターン, テキスト)
    m = re.match(pat, txt) # パターンにマッチしなかった場合はNoneを返す
    if m != None: print 'パターン"%s"はテキスト"%s"にマッチ「する」' % (pat, txt)
    else: print 'パターン"%s"はテキスト"%s"にマッチ「しない」' % (pat, txt)

txt = 'ABCDEFGH' # 探索される文字列をテキスト
                 # 探索する 文字列をパターン

# 「*」: 直前の文字の0回以上の繰り返しにマッチ
PrintRegMatch('.*CDE', txt)
PrintRegMatch('A.*H', txt)
PrintRegMatch('A*BC', txt)
PrintRegMatch('A.*BC', txt)
PrintRegMatch('A*', '')
print

# 「+」: 直前の文字の1回以上の繰り返しにマッチ
PrintRegMatch('.+DEF', txt)
PrintRegMatch('A.+H', txt)
PrintRegMatch('A+BC', txt)
PrintRegMatch('A.+BC', txt)
PrintRegMatch('A+', '')
print

# 「?」: 直前の文字の0or1回の繰り返しにマッチ
PrintRegMatch('^A?.+$', txt)
PrintRegMatch('^B?$', '')
PrintRegMatch('^C?E', 'E')
PrintRegMatch('A.?H', txt)
PrintRegMatch('BBB?C', 'BBC')

実行結果

パターン".*CDE"はテキスト"ABCDEFGH"にマッチ「する」
パターン"A.*H"はテキスト"ABCDEFGH"にマッチ「する」
パターン"A*BC"はテキスト"ABCDEFGH"にマッチ「する」
パターン"A.*BC"はテキスト"ABCDEFGH"にマッチ「する」
パターン"A*"はテキスト""にマッチ「する」

パターン".+DEF"はテキスト"ABCDEFGH"にマッチ「する」
パターン"A.+H"はテキスト"ABCDEFGH"にマッチ「する」
パターン"A+BC"はテキスト"ABCDEFGH"にマッチ「する」
パターン"A.+BC"はテキスト"ABCDEFGH"にマッチ「しない」
パターン"A+"はテキスト""にマッチ「しない」

パターン"^A?.+$"はテキスト"ABCDEFGH"にマッチ「する」
パターン"^B?$"はテキスト""にマッチ「する」
パターン"^C?E"はテキスト"E"にマッチ「する」
パターン"A.?H"はテキスト"ABCDEFGH"にマッチ「しない」
パターン"BBB?C"はテキスト"BBC"にマッチ「する」

リファレンス

Page 1 of 212

Home > Archives > 2008-07

バックナンバー
最近のコメント
最近のトラックバック
メタ情報

Return to page top