非同期処理
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の場合