@blog.justoneplanet.info

日々勉強

UITableViewCellの中にUIButtonを配置する

btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[btn setTag:BTN];
[btn setFrame:CGRectMake(200, 5, 50, 50)];
[btn setTitle:@"t" forState:UIControlStateNormal];
[btn addTarget:self action:@selector(onCellButtonPushed:event:) forControlEvents:UIControlEventTouchUpInside];
[cell.contentView addSubview:btn];

以下のようにしてどのcellのボタンが押されたか判定する。

- (void)onCellButtonPushed:(UIButton *)button event:(id)event {
    NSSet *touches = [event allTouches];
    UITouch *touch = [touches anyObject];
    CGPoint currentTouchPosition = [touch locationInView:self.tableView];
    NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint: currentTouchPosition];
    if (indexPath != nil) {
    }
}

gitで過去のcommitに対してbranchをきる

まず、コミットのハッシュ値を調べる

gitx

gitxを入れていない場合は以下のコマンドで調べる。

git log --all --graph

以下のコマンドで一度チェックアウトしたあとブランチを切った後、元のブランチをチェックアウトする。

git checkout 1234567
git branch -a
git checkout -b forgetbranch
git checkout master

addしてないファイルとかあると少し面倒になる。

androidでサービスを使う

サービスには2種類の起動方法がある。

■サービスをスタート

ServiceActivity.java

package info.justoneplanet.android.sample.service;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.widget.Toast;

public class ServiceActivity extends Activity {
    private Intent mIntent;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        // ブロードキャストレシーバの設定(サービスには直接関係しない)
        IntentFilter filter = new IntentFilter(SampleService.ACTION);
        registerReceiver(new ServiceReceiver(), filter);
    }
    
    @Override
    public void onResume() {
        super.onResume();
        
        // サービスの起動
        mIntent = new Intent(this, SampleService.class);
        startService(mIntent);
    }
    
    @Override
    public void onPause() {
        super.onPause();
        
        // サービスの停止
        stopService(mIntent);
    }
    
    /**
     * テスト用のブロードキャストレシーバ<br>
     * 実行されるとToastを表示する
     * @author justoneplanet
     */
    private class ServiceReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            Toast.makeText(getApplicationContext(), "hello", 200).show();
        }
    }
}

SampleService.java

package info.justoneplanet.android.sample.service;

import java.util.Timer;
import java.util.TimerTask;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.widget.Toast;

public class SampleService extends Service {
    public static final String ACTION = "SampleService.ACTION";
    private Timer mTimer;
    
    /**
     * サービスが一番初めに作られたときに実行される
     */
    @Override
    public void onCreate() {
        super.onCreate();
        mTimer = new Timer();
        Toast.makeText(getApplicationContext(), "onCreate", 1000).show();
    }
    
    /**
     * サービスがスタートするごとに実行される<br>
     * 但しこのメソッドは2.0以降は非推奨でonStartCommandを使うことが推奨されている
     */
    @Override
    public void onStart(Intent intent, int startId) {
        super.onStart(intent, startId);
        hello();
        Toast.makeText(getApplicationContext(), "onStart", 1000).show();
    }
    
    /**
     * サービスが無効になり取り除かれるときに実行される
     */
    @Override
    public void onDestroy() {
        super.onDestroy();
        mTimer.cancel();
        Toast.makeText(getApplicationContext(), "onDestroy", 1000).show();
    }
    
    /**
     * クライアントで登録したBroadcastReceiverに対してIntentを送る
     */
    public void hello() {
        mTimer.schedule(
                new TimerTask() {
                    @Override
                    public void run() {
                        sendBroadcast(new Intent(ACTION));
                    }
                },
                200,
                2000
        );
    }

    @Override
    public IBinder onBind(Intent intent) {
        Toast.makeText(getApplicationContext(), "onBind", 1000).show();
        return null;
    }
    
}

stopServiceを呼ばずにアプリを終了した場合は、バックグラウンドで動き続ける。

■サービスをバインド

ServiceActivity.java

package info.justoneplanet.android.sample.service;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.widget.Toast;

public class ServiceActivity extends Activity {
    private Intent mIntent;
    private SampleService mSampleService;
    
    /**
     * サービスをモニタリングするインターフェース<br>
     * 状態に応じて各メソッドがinvokeされる
     */
    private ServiceConnection conn = new ServiceConnection() {

        /**
         * サービスとの切断時に呼ばれる
         */
        @Override
        public void onServiceDisconnected(ComponentName name) {
            Toast.makeText(getApplicationContext(), "onServiceDisconnected", 1000).show();
            
        }
        
        /**
         * サービスとの接続時に呼ばれる
         */
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Toast.makeText(getApplicationContext(), "onServiceConnected", 1000).show();
            mSampleService = ((SampleService.LocalBinder)service).getService();
            mSampleService.hello();
        }
    };

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        // ブロードキャストレシーバの設定(サービスには直接関係しない)
        IntentFilter filter = new IntentFilter(SampleService.ACTION);
        registerReceiver(new ServiceReceiver(), filter);
    }
    
    @Override
    public void onResume() {
        super.onResume();
        
        // サービスの起動
        mIntent = new Intent(this, SampleService.class);
        bindService(mIntent, conn, Context.BIND_AUTO_CREATE);
    }
    
    @Override
    public void onPause() {
        super.onPause();
        
        // サービスの停止
        unbindService(conn);
    }
    
    /**
     * テスト用のブロードキャストレシーバ<br>
     * 実行されるとToastを表示する
     * @author justoneplanet
     */
    private class ServiceReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            Toast.makeText(getApplicationContext(), "hello", 200).show();
        }
    }
}

SampleService.java

package info.justoneplanet.android.sample.service;

import java.util.Timer;
import java.util.TimerTask;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.widget.Toast;

public class SampleService extends Service {
    public static final String ACTION = "SampleService.ACTION";
    private Timer mTimer = new Timer();
    
    /**
     * ActivityでサービスのインターフェースにアクセスするためのBinder
     * @author justoneplanet
     */
    public class LocalBinder extends Binder {
        public SampleService getService() {
            return SampleService.this;
        }
    }
    private IBinder mIBinder = new LocalBinder();
    
    /**
     * バインドされた時に実行される
     */
    @Override
    public IBinder onBind(Intent intent) {
        Toast.makeText(getApplicationContext(), "onBind", 1000).show();
        return mIBinder;
    }
    
    /**
     * バインドが解除されtrueを返した後、再度バインドすると実行される
     */
    @Override
    public void onRebind(Intent intent) {
        Toast.makeText(getApplicationContext(), "onRebind", 1000).show();
    }
    
    /**
     * バインドが解除されたときに実行される
     */
    @Override
    public boolean onUnbind(Intent intent) {
        Toast.makeText(getApplicationContext(), "onUnbind", 1000).show();
        mTimer.cancel();
        return true;
    }
    
    /**
     * クライアントで登録したBroadcastReceiverに対してIntentを送る
     */
    public void hello() {
        mTimer.schedule(
                new TimerTask() {
                    @Override
                    public void run() {
                        sendBroadcast(new Intent(ACTION));
                    }
                },
                200,
                2000
        );
    }
}

スレッド

Thread thread = new Thread();

■Thread

以下のようにすることで互いに別スレッドで動作するプログラムを記述できる。

class PingPong extends Thread {
    private String mWord;
    private int mDelay;
    public PingPong(String word, int delay) {
        mWord  = word;
        mDelay = delay;
    }
    @Override
    public void run() {
        try {
            for(;;) {
                System.out.println(mWord);
                Thread.sleep(delay);// 自身が実行されているスレッドで次の時間まで待つ
            }
        }
        catch(InterruptException e) {
            return;// スレッドを終了させる
        }
    }

    // スタートする
    public static void main(String[] args) {
        new PingPong("ping", 33).start();
        new PingPong("PONG", 99).start();
    }
}

但し、以下のようにRunnableを使った記述のほうが一般的ではある。

Runnable

Runnableインタフェースを実装しThreadにセットすることができる。

class RunPingPong extends Runnable {
    private String mWord;
    private int mDelay;
    public PingPong(String word, int delay) {
        mWord  = word;
        mDelay = delay;
    }
    @Override
    public void run() {
        try {
            for(;;) {
                System.out.println(mWord);
                Thread.sleep(delay);// 自身が実行されているスレッドで次の時間まで待つ
            }
        }
        catch(InterruptException e) {
            return;// スレッドを終了させる
        }
    }

    // スタートする
    public static void main(String[] args) {
        Runnable ping = new RunPingPong("ping", 33);
        Runnable ping = new RunPingPong("PONG", 99);
        new Thread(ping).start();
        new Thread(pong).start();
    }
}

コンストラクタでスレッドを開始させるとコンストラクタが実行される前にスレッドがオブジェクトのフィールドにアクセスできてしまうため危険である。

class PrintServer implement Runnable {
    private final PrintQueue q = new PrintQueue();
    public PrintServer() {
        Runnable service = new Runnable() {
        }
        new Thread(this).start();
    }

    @Override
    public void run() {
        for(;;) {
            realPrint();
        }
    }

    public void print(Print job) {
        q.add(job);
    }
    
    public void realPrint(Print job) {
        // 実際の処理
    }
}

一般的には以下のように使用される。

class PrintServer {
    private final PrintQueue q = new PrintQueue();
    public PrintServer() {
        Runnable service = new Runnable() {
            @Override
            public void run() {
                for(;;) {
                    realPrint();
                }
            }
        }
        new Thread(service).start();
    }

    public void print(Print job) {
        q.add(job);
    }
    
    public void realPrint(Print job) {
        // 実際の処理
    }
}

スレッドは生成された時に自身への参照を自身のThreadGroupに保持する。


■同期

全てのオブジェクトはロックを持っている。そのロックはsynchronizedメソッドやsynchronized文を使用することにより獲得したり解放したりできる。

以下の例では、synchronizedされたアクセサによって残高が同期なしに修正できないように保護されている。

public class BankAccount {
    private long number;
    private long balance;// 残高
    public BankAccount(long initialDeposit) {
        balance = initialDeposit;
    }
    public synchronized long getBalance() {
        return balance;
    }
    public synchronized long deposit(long amount) {
        balance += amount;
    }
}

フィールドが外部から直接アクセス可能であった場合は同期を取る必要がない。

  • 各メソッドは、相互排他して実行される
  • synchronized宣言により、2つ以上の実行しているスレッドがお互いに干渉しないことが保証される
  • メソッド呼び出しの1つが実行を開始したら、他からは最初のメソッドの呼び出しが完了するまで開始できない
  • 操作の順序には保証はない
  • 拡張したクラスがsynchronizedメソッドをオーバーライドしたときにsynchronizedにしないこともできる

コンストラクタは同期をとる必要はないしsynchronizedを宣言できない

オブジェクトが生成される時だけ実行され新たなオブジェクトを生成する際には1つのスレッド内だけで実行される。

staticのsynchronizedメソッド

staticなフィールドのデータがスレッド間で共有されている場合、staticでsynchronizedされたメソッドからアクセスする必要がある。この時、自身に関連付けられたClassオブジェクトに対してロックがかかるが、synchronizedされたメソッドに対してのinvokeのみブロックの対象となる。

ロックの確認

以下のようにすることで現在のスレッドがオブジェクトに対してロックを保持しているか判定できる。

Thread.holdLock(this);

synchronized文

以下のようにすることでカレントオブジェクトではなく任意のオブジェクトに対してのロックを獲得できる。但し、exprはオブジェクトの参照を返す必要がある。

synchronized(expr) {
    statements
}

synchronizedメソッドはthisへの参照を伴うsynchronized文であり省略した表現である。

public static void abs(int[] values) {
    synchronized(values) {
        for(int i = 0; i < values.length; i++) {
            if(values[i] < 0){
                values[i] = -values[i];
            }
        }
    }
}
synchronized文のメリット
  • 必要な箇所に限定された(フィールドへの代入だけなどの)処理を定義しロックの時間を短くできる
  • 同クラス内でもthis以外の異なったオブジェクトで同期を取ることが可能

用例

エンクロージングオブジェクトに対して内部オブジェクトが同期を取る。

public class Outer {
    private int data;
    public class Inner {
        void setOuterData() {
            synchronized(Outer.this) {
                data = 1;
            }
        }
    }
}

コンストラクタ内で使用される。

class Body {
    Body() {
        synchronized(Body.class) {
            idNum = nextID++;
        }
    }
}

■設計

クラスに対して適切な同期を設計することは複雑で難しい。

  • synchronized文は全てのクライアントが正しい処理を行うことに依存するので(クライアント同期)、オブジェクトへのアクセスメソッドをsynchronizedとする方が強固である(サーバー同期)
  • 複数のメソッド呼び出しについてアトミックである必要がある場合、1つのメソッドに一連のメソッドをまとめる方法は設計の観点から一般的とはいえない
  • 複数のメソッド呼び出しについてアトミックである必要がある場合、synchronized文でクライアント側同期しか選択肢がない場合がある
class Util {
    private int value;
    Util(int initial) {
        value = initial;
    }
    public synchronized add(int x) {
        value += x;
        System.out.println("value:" + value);
    }
}
// Util util = new Util(5);
// thread a : util.add(7);
// thread b : util.add(9);
// 5 12 21
// 5 14 21
class Util {
    private static int value = 0;
    public synchronized static add(int x) {
        value += x;
        System.out.println("value:" + value);
    }
}
// thread a : Util.add(7);
// thread b : Util.add(9);
// 0 7 16
// 0 9 16
class Util {
    private static int value = 0;
    public synchronized static add(int x) {
        value += x;
        System.out.println("value:" + value);
    }
    public static subtract(int x) {
        add(-x);
    }
}

■スレッド間通信

wait

条件を待つスレッドは以下のように記述する。

synchronized void doWhenCondition() {
    while(!condition) {// whileをifにしてはいけない
        wait();
    }
    // 処理
}

スレッドを(一時)停止させるときにwaitはオブジェクトのロックをアトミックに解放する。(スレッドを停止した場合にロックの解放までがアトミックである)

notifyAll, notify

以下のようにして他のスレッドに通知する。

synchronized void changeCondition() {
    // 条件で使用される値の変更処理
    notifyAll();// notify();
}

notifyAllを使用するのは以下の条件のときである。

  • 同じ条件に対して全てのスレッドが待っている
  • 変更された条件で作用があるのは1つのスレッドのみである
  • 全てのサブクラスに対して契約的に成り立つ

notifyAllを使うことによって(superクラスの)notifyが機能しなくなる可能性がある。

notifyAllの用例
class PrintQueue {
    private SingleLinkQueue<PrintJob> queue = new SingleLinkQueue<PrintJob>();

    /**
     * プリントキューの追加<br>
     * 他の待っているスレッドに通知する
     */
    public synchronized void add(PrintJob job) {
        queue.add(job);
        notifyAll();
    }
    
    /**
     * 他のスレッドが何か挿入するまで待つ
     */
    public synchronized PrintJob remove() throws InterruptedException {
        while(queue.size() == 0) {
            wait();// プリントジョブが追加されるのを待つ
        }
        return queue.remove();
    }
}

各メソッドについて

wait

currentスレッドは以下の4つのケースまで待つ。

  • notifyがオブジェクトに対してcallされcurrentスレッドが実行可能になるまで
  • notifyAllがオブジェクトに対して呼ばれるまで
  • 指定されたtimeout時間まで
  • スレッドに対してinterruptメソッドがcallされるまで
public final void wait(long timeout) throws InterruptedException
上述の4つの条件までwaitする
public final void wait(long timeout, int nanos) throwa InterruptException
timeoutの時間がtimeout(ミリ秒) + nanos(ナノ秒)で指定できる
public final void wait()
wait(0)と同様

タイムアウトか条件を満たしたか知る必要がある場合は経過時間を計測する必要がある。

通知
public final void notifyAll()
条件の変化を待っている全てのスレッドに対して通知する
public final void notify()
条件の変化を待っている1津のスレッドに対して通知する。対象スレッドを選択することはできないので、待っているスレッドが単数に確実に絞れる場合にだけ使用する

ロックが保持されていない時に使用するとIllegalMonitorStateExceptionが投げられる。

■スケジューリング

実行しているスレッドは以下の条件のどれかまで実行を続ける。

  • wait
  • sleep
  • I/O
  • (タイムスライスなど)プリエンプションされるまで
  • VM依存
  • 共有リソースへのアクセスは常に保護する必要がある
  • 全ての順序はシステムに依存し特定の順序を期待するコードを書いてはいけない

各メソッドについて

public static void sleep(long millis) throws InterruptedException
現在実行中のスレッドを指定された秒数(以上)の間スリープさせる。スレッドがスリープ中に割り込まれるとIntterruptedExceptionが発生する
public static void sleep(long millis, int nanos) throws InterruptedException
millis + nanos秒の間(以上)の間スリープさせる。
public static void yield()
currentスレッドを実行させる必要がない事をスケジューラに知らせる。

スレッドの優先順位について

public final void setPriority(int newPriority)
Thread.MIN_PRIORITY〜Thread.MAX_PRIORITYで設定する
public final int getPriority()
スレッドの優先順位を返す

UIスレッドはNORM_PRIORITY+1で実行され、他のスレッドはNORM_PRIORITY-1で実行されることが多い。

■デッドロック

ランタイムはデッドロックを検出したり防いだりはしないので、設計でデッドロックの可能性を熟考する必要がある。

リソース順序付け

ロックを獲得するオブジェクトに関して常に同じ順序でロックを取得するようにする。

■スレッドの実行の終了

以下の条件の時にスレッドは終了する。

  • runメソッドが正常に戻る
  • runメソッドが突然完了する(例外が発生しキャッチされない場合など)
  • アプリケーションが終了する

スレッドのキャンセル

キャンセルは割り込みによって実現される。

thread2.interrupt();

thread2では以下のような処理が記述されているとする。

void tick(int count, long pauseTime) {
    try {
        for(int i = 0; i < count; i++) {
            System.out.println(',');
            System.out.flush();
            Thread.sleep(PauseTime);
        }
    }
    catch(InterruptedException e) {// 他のスレッドから割り込みされた時
        Thread.currentThread().interrupt();
    }
}

下述の2点に注意して強調しているコードがマルチスレッドを生かして処理できるようにする必要がある。

  • 割り込みを隠蔽しない
  • InterruptedExceptionをキャッチして正常に処理を進めようとしない

■スレッドの完了を待つ

class CalcThread extends Thread {
    private double result;
    @Override
    publc void run() {
        result = calculate();
    }
    publc double getResult() {
        return result;
    }
    publc double calculate() {
        // calc
    }
}
class ShowJoin {
    public static void main(String args[]) {
        CalcThread calc = new CalcThread();
        calc.start();
        doSomethingElse();
        try {
            calc.join();// joinが戻ってきたときにはrunが終了されていることが保証される
            System.out.println("result is " + calc.getResult());
        }
        catch(InterruptedException e) {
            System.out.println("No answer: interrupted");
        }
    }
}

各メソッドについて

public final void join(long millis) throws InterruptedException
スレッドが終了するか指定した時間が経過するまで待つ
public final void join(long millis, int nanos) throws InterruptedException
スレッドが終了するか指定した時間(millis + nanos)が経過するまで待つ
public final void join() throws InterruptedException
join(0)と同様

■アプリケーション実行を終了させる

スレッドの種類

  • user
  • demon

最後のユーザスレッドが終了するとデーモンスレッドは消滅させられてアプリケーションは終了する。

■メモリモデルとvolatile

同期順序は常にプログラム順序と整合性が取れる。単一のスレッドが更新し多数のスレッドから読み取る場合は同期の必要がないが、多数のスレッドが更新した値にアクセスするような場合は同期が必要でvolatile修飾子を検討する。

各メソッドについて

public Thread(ThreadGroup group, String name)
セキュリティ上可能なスレッドグループ内で与えられた名前を持つスレッドを生成する
public ThreadGroup(String name)
currentスレッドのグループを親グループとする新たなスレッドグループを生成する
public ThreadGroup(ThreadGroup parent, String name)
親グループを指定できる
public final String getName()
ThreadGroupの名前を返す
public final ThreadGroup getParent()
親のスレッドグループを返す
public final void setDaemon(boolean daemon)
カレントスレッドグループにデーモンステータスを設定する
public final boolean isDaemon()
カレントスレッドグループのデーモンステータスを返す
public final void setMaxPriority(int maxPri)
カレントスレッドグループの最大優先順位を設定する
public final int getMaxPriority()
カレントスレッドグループの最大優先順位を取得する
public final boolean parentOf(ThreadGroup group)
カレントスレッドグループがgroupの親であるか判定する
public final void checkAccess()
カレントスレッドがグループの修正を許されているか判定する
public final void destroy()
カレントスレッドグループを破棄する。スレッドが含まれていない事が前提で含まれていた場合はIllegalThreadStateExceptionを投げる
public int activeCount()
全てのサブグループのアクティブなスレッドを概算する
public int enumerate(Thread[] threadsInGroup, boolean recurse)
グループ内の全てのアクティブなスレッドへの参照を返す
public int enumerate(Thread[] threadsInGroup)
グループ内の全てのアクティブなスレッドへの参照を返す(再帰的)
public int activeGroupCount()
全てのサブグループのグループ数をカウントする
public int enumerate(ThreadGroup[] groupInGroup, boolean recurse)
グループ内の全てのアクティブなスレッドグループへの参照を返す
public int enumerate(ThreadGroup[] groupInGroup)
グループ内の全てのアクティブなスレッドグループへの参照を返す(再帰的)
public static int activeCount()
カレントスレッドのThreadGroup内のアクティブなスレッドの数を返す
public static int enumerate(Thread[] threadsInGroup)
グループ内の全てのアクティブなスレッドへの参照を返す
public void uncaughtException(Thread thread, Throwable exception)
threadがthrowされたexceptionにより終了するときに呼び出される

■デバッグ

public String toString()
スレッド名、優先順位、スレッドグループ名の文字列を返す
public Thread.State getState()
スレッドの現在の状態を返す{NEW, RUNNABLE, BLOCKED, WATING, TIMED_WATING, TERMINATED}
public static void dumpStack()
カレントスレッドのスタックトレースをSystem.errへ表示する
public String toString()
ThreadGroupの文字列表現を返す
public void list()
ThreadGroupを再帰的にリストしてSystem.outに表示する

reboot時にコマンドを実行する

以下のコマンドでファイルを編集する。

vi /etc/rc.d/rc.local

以下のような記述があると思う。

touch /var/lock/subsys/local

最終行に起動時に実行したいコマンドを記述する。

ulimit -n 32768
/home/hogehoge/fugafuga/check.sh > /home/hogehoge/fugafuga/result.txt &

macからgithubを使う

そーいやmacで使ってなかったので一応メモ。

cd ~/.ssh
ssh-keygen -t rsa -C "user@hogehoge.com"
Generating public/private rsa key pair.
Enter file in which to save the key (/var/root/.ssh/id_rsa): 
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /var/root/.ssh/id_rsa.
Your public key has been saved in /var/root/.ssh/id_rsa.pub.

以下のコマンドで内容をメモる。

vi id_rsa.pub

Account Settingsのキーの方にペーストする。

ssh -T git@github.com
Hi hogehoge! You've successfully authenticated, but GitHub does not provide shell access.

httpd restartでAddress already in useが表示されたとき

以下のコマンドでapacheを再起動する。

/etc/init.d/httpd restart

以下のようなメッセージが表示されて再起動できなくなる。

Starting httpd: (98)Address already in use: make_sock: could not bind to address [::]:80

以下のコマンドでポートを使用しているプロセスを調べる。

/usr/sbin/lsof -i | grep http

プロセス番号が表示されるのでkillする。

crontabとCakePHPでバッチ処理をする

バッチ処理したい時などに使う技である。

■実装

app/vendors/shells/calc.php

以下のようにしてShellクラスを継承する。基本的にはコントローラと同様にモデルなどが使用できるがコンポーネントについては注意が必要である。

date_default_timezone_set('Asia/Tokyo');

//Configure::write('Config.environment', isset($_SERVER['CAKE_ENV']) ? $_SERVER['CAKE_ENV'] : "development");// コマンドラインから叩いている場合、$_SERVERによる環境分岐ができない
Configure::write('Config.environment', "production");

class RankShell extends Shell
{
    public $uses = array(
        'Logs',
        'Ranks'
    );
    
    /**
     * 処理を実行する前に読み込むコンポーネントなどを記述する
     */
    public function initialize()
    {
        parent::initialize();
        //$this->Email = new EmailComponent($this);// コンポーネント名に注意
    }
    
    /**
     * ここに記述した処理が実行される
     */
    public function main()
    {
        $this->out("start");
        if($result = $this->Log->getRank()){
            $this->Ranks->deleteAll();
            if($this->Ranks->saveAll($result)){
                $this->out("success");
            }
            else{
                $this->out("failed to save");
            }
        }
        else{
            $this->out("failed to calc");
        }
    }
}

■実行

以下のようにして実行できるかどうか確認する。

/usr/bin/php /var/www/hogehoge.justoneplanet.info/cake/console/cake.php calc

■crontab

以下のコマンドを実行してcrontabを編集する。

crontab -e

例えば10時のオヤツを忘れないよう10:15に実行するようにするには以下のようにする。

15 10 * * * /usr/bin/php /var/www/hogehoge.justoneplanet.info/cake/console/cake.php calc

15分ごとに処理をするには以下のように記述する。

*/15 * * * * /usr/bin/php /var/www/hogehoge.justoneplanet.info/cake/console/cake.php calc

以上のようにCakePHPでは非常に簡単にバッチ処理を書くことができる。個人的にはZendFrameworkよりも簡単に感じる。