非同期処理

C#

★時間がかかる処理に対する改善方法

オリジナルメソッド

public int Add(int a, int b)

{

Thread.Sleep(2000); // ←時間がかかる処理

return a + b;

}

呼び出す方法1(塞がることは発生するので、実用できない)

/// <summary>

/// 同期 ×

/// </summary>

public void SyncInvoke()

{

Func<int, int, int> handler = Add;

int result = handler.Invoke(1, 2); // ←塞がる

Console.WriteLine("Do other work... ... ...");

Console.WriteLine(result);

Console.WriteLine("Over");

}

/// <summary>

/// 非同期 ×

/// </summary>

public void AsyncInvoke()

{

Func<int, int, int> handler = Add;

IAsyncResult result = handler.BeginInvoke(2, 3, null, null);

Console.WriteLine("Do other work... ... ...");

//★書き方1

//実行完了かどうか

while (result.IsCompleted == false)

{

Console.WriteLine("wait...");

Thread.Sleep(500);

}

//★書き方2

//実行完了まで待つ

while (true)

{

Console.WriteLine(".");

if (result.AsyncWaitHandle.WaitOne(500, false))

{

break;

}

}

Console.WriteLine(handler.EndInvoke(result)); // ←塞がる

Console.WriteLine("Over");

}

呼び出す方法2(時間がかかる処理を待たず、以降の処理は同時に実行できる)

/// <summary>

/// 非同期Callback式 ○

/// </summary>

public void AsyncCallback()

{

Func<int, int, int> handler = Add;

//★書き方1

IAsyncResult result =

handler.BeginInvoke(3, 4, new AsyncCallback(AddComplete), "AsycState:OK!");

//★書き方2 AsyncResultを呼ぶ必要がない

IAsyncResult result2 = handler.BeginInvoke(5, 6,

ar =>

{

Console.WriteLine(handler.EndInvoke(ar));

},

null);

Console.WriteLine("Do other work... ... ...");

Console.WriteLine("Over");

}

private void AddComplete(IAsyncResult result)

{

Func<int, int, int> handler =

(Func<int, int, int>)((AsyncResult)result).AsyncDelegate;

Console.WriteLine(handler.EndInvoke(result));

Console.WriteLine(result.AsyncState);

}

★非同期処理を呼び出す方法

オリジナルメソッド

//★リターン値なし

public void SayHello()

{

Thread.Sleep(1000);

Console.WriteLine("SayHello");

}

//★リターン値あり

public string GetHello()

{

Thread.Sleep(1000);

return "GetHello";

}

Thread式

public void ThreadAsync()

{

//★リターン値なし

var newThread = new Thread(SayHello);

newThread.Start();

//★リターン値あり

var strHello = string.Empty;

var newThread2 = new Thread(() => strHello = GetHello());

newThread2.Start();

Console.WriteLine("Value:" + strHello);

while (true)

{

if (!string.IsNullOrEmpty(strHello))

{

Console.WriteLine("Value2" + strHello);

break;

}

}//while

}

Delegate式

public void DelegateAsync()

{

//★リターン値なし

Action action = SayHello;

var async = action.BeginInvoke(null, null);

action.EndInvoke(async);

//★リターン値あり

Func<string> action2 = GetHello;

var async2 = action2.BeginInvoke(null, null);

var hello = action2.EndInvoke(async2);

Console.WriteLine(hello);

}

Java

joinによるブロッキング

public static String getCurThreadName() {

return Thread.currentThread().getName();

}

static class HotWaterThread extends Thread {

public HotWaterThread() {

super("Hot-Water-Thread");

}

@Override

public void run() {

try {

...

} catch (InterruptedException e) {

...

}

}

}

static class WashThread extends Thread {...}

Thread hThread = new HotWaterThread();

Thread wThread = new WashThread();

hThread.start();

wThread.start();

Thread.currentThread().setName("Main-Thread");

try {

hThread.join();

wThread.join();

} catch (InterruptedException e) {

Print(getCurThreadName() + " interrupted!");

}

Print(getCurThreadName() + " done.");

futureTaskによるブロッキング

import java.util.concurrent.Callable;

import java.util.concurrent.ExecutionException;

import java.util.concurrent.FutureTask;

static boolean waterOk = false;

static boolean cupOk =false;

static class HotWaterJob implements Callable<Boolean>{

@Override

public Boolean call() throws Exception {

try{

...

} catch (InterruptedException e){

...

return false;

}

return true;

}

}

static class WashJob implements Callable<Boolean>{...}

Callable<Boolean> hJob = new HotWaterJob();

FutureTask<Boolean> hTask =

new FutureTask<Boolean>(hJob);

Thread hThread = new Thread(hTask, "Hot-Water-Thread");

Callable<Boolean> wJob = new WashJob();

FutureTask<Boolean> wTask =

new FutureTask<Boolean>(wJob);

Thread wThread = new Thread(wTask, "Wash-Thread");

hThread.start();

wThread.start();

Thread.currentThread().setName("Main-Thread");

try{

waterOk = hTask.get();

cupOk = wTask.get();

...

} catch (InterruptedException | ExecutionException e){

Print(getCurThreadName() + " interrupted!");

}

Print(getCurThreadName() + " done.");

callbackによる非ブロッキング

import com.google.common.util.concurrent.*;

import java.util.concurrent.*;

static boolean waterOk = false;

static boolean cupOk = false;

static class HotWaterJob implements Callable<Boolean>{...}

static class WashJob implements Callable<Boolean>{...}

Thread.currentThread().setName("Main-Thread");

Callable<Boolean> hJob = new HotWaterJob();

Callable<Boolean> wJob = new WashJob();

ExecutorService jPool =

Executors.newFixedThreadPool(10);

ListeningExecutorService gPool =

MoreExecutors.listeningDecorator(jPool);

ListenableFuture<Boolean> hFuture = gPool.submit(hJob);

ListenableFuture<Boolean> wFuture = gPool.submit(wJob);

Futures.addCallback(hFuture, new FutureCallback<Boolean>(){

@Override

public void onSuccess(Boolean r){

if (r){

waterOk = true;

...

}else{

...

}

}

@Override

public void onFailure(Throwable t){...}

});

Futures.addCallback(wFuture, new FutureCallback<Boolean>(){...});

try{

...

} catch (InterruptedException e){

Print(getCurThreadName() + " interrupted!");

}

Print(getCurThreadName() + " done.");

gPool.shutdown();

JavaScript

JavaScriptはsingle threadである

方法1 属性

デメリット: 処理間の依存性が高い、Overrideされやすい

element.onclick=function(){ ... }

方法2 コールバック

デメリット: Coupling、Callbackが一つしか持てない、コールバック地獄、例外がキャッチされない、並列処理をしにくい

element.addEventListener("click", function(){ ... })

f1(f2);

function f1(callback){

setTimeout(function(){

// do something

callback();

}, 500);

}

方法3 Promise(ES6)

デメリット: 同期処理のように非同期処理を書きにくい

var promise = new Promise(function(resolve, reject) {

if (/* 成功 */){ resolve(value); }

else { reject(error); }

});

promise

.then((result) => { ··· })

.catch((error) => { ··· })

Promise.all(···)

Promise.race(···)

Promise.reject(···)

Promise.resolve(···)

方法4 Generator(ES6)

デメリット: coなどを利用する以外、next()コールが必須

import co from 'co';

co(function* (){

let result= yield [

(yield fetch('https://...')).json(),

(yield fetch('https://...')).json()

];

console.log("result:",result);

});

方法5 awaitとasync(ES7)

(async function (){

let result= await Promise.all([

(await fetch('https://...')).json(),

(await fetch('https://...')).json()

]);

console.log("result:",result);

})();

(async function (){

try{

let result= await fetch('https://...');

let json =await result.json();

console.log("result:",json);

} catch(ex){

console.warn("warn:",ex);

}

})();

改善前

async function getABC() {

let A = await getValueA();

let B = await getValueB();

let C = await getValueC();

return A*B*C;

}

改善後 ↓

async function getABC() {

let results = await Promise.all([ getValueA, getValueB, getValueC ]);

return results.reduce((total,value) => total * value);

}

JQuery

方法1 Listener

メリット:複数リスナーをバインドできる、Decoupling、モジュール化

f1.on('done', f2);

function f1(){

setTimeout(function(){

// do something

f1.trigger('done');

}, 500);

}

方法2 Publish-Subscribe

メリット:管理・監視しやすい

jQuery.subscribe("done", f2);

function f1(){

setTimeout(function(){

// do something

jQuery.publish("done");

jQuery.unsubscribe("done", f2);

}, 500);

}

方法3 Promises

メリット: Chainのような書き方でわかりやすい

f1().then(f2);

f1().then(f2).then(f3);

f1().then(f2).fail(f3);

$.when(f1()).done(f2).fail(f3);

或いは

$.Deferred(f1).done(f2).fail(f3);

function f1(){

var dfd = $.Deferred();

setTimeout(function(){

// do something

dfd.resolve(); // done()を呼び出す

//deferred.reject() // fail()を呼び出す

}, 500);

return dfd.promise;

}

JavaScriptの場合