2017年4月28日金曜日

C# VisualStudio for Mac でGtk#を使ってみた。gtk-sharp

「VisualStudio for Mac」を入れてはみたが、
MacのGUI作るのに「Gtk#」使っている人を見ないのでちょっと触ってみました。
(「Xamarin.Form」はMac非対応です。
 「Xamarin.Mac」から「cocoa」のUIを使った方がMacライクなUIになります。)
「Gtk#」は「Gtk+」のC#版です。
ついでに「Gtk#」を使って、
Windowsとのクロスプラットフォーム開発がどの程度できるかテストしてみます。


Macで「Gtk#」を使ってみる


とりあえず新しいプロジェクトを作成。
「Gtk#」を選びます。

私は「testGtk」の名前で新規プロジェクトを作成しました。

「ユーザーインターフェィス」の「MainWindow」を選択すると、

無事に「デザイナ」が立ち上がりました。
コントロールを配置します。
「ツールボックス」から「Fixed」を配置。
続いて「Button」と「Label」を配置します。
「button1」のプロパティを変更します。
「Button Properties」の「Label」を
「GtkButton」から「開始」に変更して下さい。

「デザイナ」の下にチラ見している「ソース」を選択すると、
ソースコードが表示されます。

「MainWindow」クラス内の一番下に以下のコードを追加します。
以下のコードをクラス内へ追加
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//ボタンクリックイベント
protected void ButtonClicked_Event(object sender, EventArgs e)
{
  var task = System.Threading.Tasks.Task.Run(new System.Action(()=>{
    for (int i = 0; i < 6; i++)
    {
      label1.Text = zenRep(i.ToString()) + "秒";
      System.Threading.Thread.Sleep(1000);
    }
  }));
}
 
// 半角文字列を全角文字列へ変換
private string zenRep(string s)
{
  var resultStr = s;
  resultStr = System.Text.RegularExpressions.Regex.Replace(resultStr, "[0-9]", (p) => ((char)(p.Value[0] - '0' + '0')).ToString());
  resultStr = System.Text.RegularExpressions.Regex.Replace(resultStr, "[A-Z]", (p) => ((char)(p.Value[0] - 'A' + 'A')).ToString());
  return resultStr;
}

コードを追加したら、表示を「デザイナ」に戻して
「ボタン」の「シグナル」の「clicekd」イベントの「ハンドラー」へ
クリックイベントのメソッド名を入れます。
「ButtonClicked_Event」と入力して下さい。



これで完成です。
「コマンド+エンター」等で「実行」してみましょう。
以下のようなFormが表示されます。



開始ボタンを押してみると…

無事「0秒」〜「5秒」までカウントされます。



クラスプラットフォーム化


クロスプラットフォームで動くかテストする為に、
完成した「VisualStudio for Mac」の「ソリューション」をフォルダごと
「Windows」側へコピーするか、共有して下さい。

次に、
「Gtk#for.NET」を「Windows」にインストールします。
「Windows」で以下のサイトへ行きます。
http://www.mono-project.com/download/#download-win
以下のファイルをダウンロード。


インストールを進めて下さい。

問題なくインストールが終了したら、
「Windows」の「VisualStudio」で「ソリューション」を開き、
実行してみて下さい。


「Windows」側でも無事動きました。
「0秒」〜「5秒」までカウントされます。
非同期処理も問題無く動いています。


感想


簡単な物であればすぐ実用に使えそうな気がします。
UIの見え方は「Gtk#」独特の物となりますが、
「Mac」と「Windows」のUIを共通で気軽に作れるのは凄く魅力的です。
ただ今の所、
プラットフォーム毎のショートカットの制御がわかりません。
誰か教えて下さい…

「Gtk#」のチュートリアルはここ。
http://www.mono-project.com/docs/gui/gtksharp/tutorials/

「Windows」側でのGUIデザイナ用エディタについて…
「Stetic」→「Monodevelop」「Xamarin Studio」に含まれている。「Gtk2」のみ対応
「Glade」→「Gtk2」「Gtk3」両方に対応
「VisualStudio」に組み込まれたGUIデザイナは今の処存在しない。
直接コードで書く事も選択肢に入るかも…。
http://grbd.github.io/posts/2016/01/29/gtksharp-part-2-setting-up-glade/

補足
「Gtk#」でダイアログを表示させる。
ダイアログ表示
//「Gtk#」のダイアログ
//「using」は使えないので注意
var md = new MessageDialog(
    this, DialogFlags.Modal, MessageType.Info,
    ButtonsType.Ok, "てすと\r\nテスト");
md.Run();
md.Destroy(); //←「Dispose()」は使えないので注意
ちょっと面倒な印象。
参考→http://7shi.hateblo.jp/entry/2012/04/24/233941



個人的メモ…

MacOS10.11.6上でlabelの半角欧文が文字化けした際、
解決はしなかったが勉強になったサイト。
「Gtk#」の日本語が文字化けに関する対処が書かれている。
http://kki-zakki.blogspot.jp/2008/12/monodevelop-20-alphamac-os-x.html

https://discussionsjapan.apple.com/thread/10052914?start=0

「gtkrc」ファイルをいじる
→「Visual Studio.app/Contents/Resources/lib/monodevelop/bin」
http://reiji1020.hatenablog.com/entry/2012/07/12/203443

フォント「サンフランシスコ」入れてもダメだった
https://www.ipentec.com/document/document.aspx?page=software-font-download-apple-san-francisco
https://developer.apple.com/fonts/

欧文系は全部試してダメだったので、たぶん日本語フォントっぽい。

「SuitcaseFusion」の問題だったみたい。
「Helvetica」をシステムから引っ張り出して、
「SuitcaseFusion」へ登録し直したら解決した。


winとmacのクロスプラットフォームなGUIならば
「electron.net」を検討してみるのも良いかも


teratail:Macで開発できるまでの手間が少なくて、
MacとWindowsで動作するGUIの
Twitterクライアントを作るにはどの言語が良いでしょうか。
https://teratail.com/questions/65420

2017年4月27日木曜日

C# NuGetのVisualSutudioとfor Macの管理の違い

「NuGet」は「C#」でパッケージ管理を行ってくれます。
依存関係のあるパッケージもインストールしてくれるし、
依存パッケージが非対応バージョンの時は最新バージョンへの自動更新も行います。

「Windows」と「Mac」で「VisualStudio」を使う場合、
まぁ、たいした違いはないのですが、
「NuGet」で管理にほんの少し違いがあるのでまとめてみました。



「Windows」の「NuGet」管理


プロジェクトで「右クリック->NuGetパッケージの管理->参照」
で任意の「NuGetパッケージ」を追加します。

「packages」フォルダ…
「VisualStudio」の「.sln」ソリューションファイルと同階層に
「packages」フォルダは作成されます。
「VisualStudio」側から直接「packages」フォルダの確認はしません。
プロジェクトの「参照」から確認します。
「packages」フォルダ内には依存関係も含めた各々のパッケージが、
「各パッケージ名」のフォルダとして展開されます。
「packages」フォルダは手動で削除しても問題ありません。
(ビルド時に自動で再ダウンロードされます)

「参照」内…
プロジェクトの「参照」から
追加された「パッケージ」(DLL)の存在を確認できます。
(ダブルクリックで「オブジェクトブラウザー」が見れます)

「packages.config」…
プロジェクトに「packages.config」XMLファイル作成されます。
追加したパッケージやバージョン情報が書き込まれたファイルです。
勝手に編集しない事。編集不可。



「Mac」の「NuGet」管理


プロジェクトで「右クリック->追加->NuGetパッケージの追加」
で任意の「NuGetパッケージ」を追加します。
追加処理が終わったら念のために、
プロジェクトの「参照」と「パッケージ」を、
それぞれ右クリックして「最新の情報に更新」を行っておきます。

「packages」フォルダ…
「VisualStudio」の「.sln」ソリューションファイルと同階層に
「packages」フォルダは作成されます。
「VisualStudio」側から直接「packages」フォルダの確認はしません。
プロジェクトの「パッケージ」から確認します。
「packages」フォルダ内には依存関係も含めた各々のパッケージが、
「各パッケージ名」のフォルダとして展開されます。
「packages」フォルダは手動で削除しても問題ありません。
(ビルド時に自動で再ダウンロードされます)

「参照/パッケージから」内…
プロジェクトの「参照/パッケージから」から
追加された「パッケージ」(DLL)の存在を確認できます。
(ダブルクリックで「アセンブリブラウザー」が見れます)
管理はプロジェクトの「パッケージ」から行います。

「パッケージ」内…
プロジェクトの「パッケージ」から
追加された「パッケージ」(DLL)の存在を確認できます。
(ダブルクリックで「アセンブリブラウザー」が見れます)
ここから自分で追加した各パッケージの管理ができます。
・「パッケージ」を右クリック…
  「パッケージの追加」→プロジェクトに新しい「NuGetパッケージ」追加。
  「更新」→「NuGetパッケージ」に新バージョンがあればアップデート。
  「復元」→破損した「NuGetパッケージ」を再ダウンロード。
       (新バージョンへのアップデートは行われない。)
・「パッケージ」階層下の更新したいパッケージを右クリック…
  「更新」→「NuGetパッケージ」に新バージョンがあればアップデート。
  「削除」→「NuGetパッケージ」を削除。関連「パッケージ」(DLL)も削除。 

「packages.config」…
プロジェクトに「packages.config」XMLファイル作成される。
追加したパッケージの情報が書き込まれる。
勝手に編集しない事。編集不可。




以下のサイトを参考にしています。
https://techinfoofmicrosofttech.osscons.jp/index.php?NuGet%20%E3%82%92%E4%BD%BF%E7%94%A8%E3%81%97%E3%81%9F%E3%83%91%E3%83%83%E3%82%B1%E3%83%BC%E3%82%B8%E7%AE%A1%E7%90%86
https://www.xlsoft.com/jp/products/xamarin/nuget_walkthrough.html





2017年4月24日月曜日

貼り付け用ソースコード生成ツール(SyntaxHighlighter用)

「SyntaxHighlighter」用コード生成ツール(解説は下の方)

  
   
  <pre class="brush:text" title="">
↓「テキストエリア」に「コード」入力後 →「変換」ボタン押下

C


 「<」を「&lt;」へ、「>」を「&gt;」へ置換します。
・この頁をブックマークして使用して下さい。
・「ショートカット」での「変換」も可能。
 「Windows → ALT+C」/「MAC → CTRL+Alt+C」で「変換」できます。
・「変換」時、先頭行と最終行の空改行は自動で削除されます。
・「開始行番号変更」使用時は「first-line:1」の数字を任意の開始行番号へ書き換えて下さい。
・「行ハイライト」使用時は「highlight:[1,3]」など、
 ハイライトしたい行番号を自分で書き足して使用して下さい。
・動作にはJavaScriptを使用しています。
・「開始行番号変更」機能以外の変換設定はクッキーへ記録されます。
・このツールは作者自身が使いやすい形に調整しております。
 コメントに捨て台詞を書くと作った私が少し嬉しくなります。 
「Blogger」への「SyntaxHighlighter」設置方法等は、
以下に書いてあります。
Bloggerでソースコードを貼り付ける(改改)
http://1studying.blogspot.com/2017/04/blogger_93.html




2017年4月22日土曜日

C# マルチスレッド、非同期の補足

C# 今時のマルチスレッド、非同期のやり方
http://1studying.blogspot.com/2017/04/c.html
の補足情報です。
補足では、
「親スレッド」→「メインスレッド」
「子スレッド」→「ワーカースレッド」
として、正しい名称で説明しています。

「Task.Wait()」と「デットロック」と「Task.Result」


リソースの待ち合いによる「デットロック」
「Task.Wait()」は「Task」の処理終了を待つ間、
「メインスレッド」の動作を止めてしまいます。
その時に、
「ワーカースレッド」側から「Invoke」で
「メインスレッド」側の「Form1」内を触ろうとしても、
「メインスレッド」側は「ワーカースレッド」の処理終了まで
動作を止めている為、「メインスレッド」の動作が再開する迄
「ワーカースレッド」も処理を止めます。
すると、
「メインスレッド」も「ワーカースレッド」も、
永遠に処理が進まなくなります。これが、「デットロック」です。
「Invoke」と「Task.Wait()」による「デットロック」の例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
private void button1_Click(object sender, EventArgs e)
{
    //「ワーカースレッド」で実行
    var task = Task.Run(() =>
    {
        //「0」~「5」迄カウントアップ
        for (int i = 0; i < 6; i++)
        {
            //「メインスレッド」側で処理を行ってもらうコードを記述
            //「メインスレッド」側での処理が終了するまでの間は、
            //「ワーカースレッド」の処理を止めて待機
            this.Invoke(new Action(() => {
                //「label1」の表示更新
                label1.Text = i.ToString();
            }));
            //1秒待つ
            System.Threading.Thread.Sleep(1000);
        }
    });
 
    //ここは「非同期」なので「Form1」側の処理は止まりません。
 
    //↓ここで「同期処理」的になり「Form1」の処理が止まる。
    //「ワーカースレッド」の処理が終わるまで、
    //「メインスレッド」の処理を止めて待機
    task.Wait();
    //「デットロック」の為、ここまで処理が来ない。
    MessageBox.Show("処理終了");
}
実行すると「Form1」がハングアップしたような状態になります。
「Task.Wait()」と「Invoke」は相性が悪い。
「async/await」を使った方が良いです。


「Task.Wait()」の「非同期処理」化と「デットロック」回避…
通常は「async/await」を使った方が良いのですが、
「Task.Wait()」を残した方法だと、
「非同期」の処理を全て「タスク」の中で行う方法があります。
「Task.Wait()」での「非同期処理」化例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
private void button1_Click(object sender, EventArgs e)
{
    //「タスク」内の処理終了を待たずに、次の行へ処理が移ります。「非同期」
    var task1 = Task.Run(() =>
    {
        //「0」~「5」迄カウントアップ
        for (int i = 0; i < 6; i++)
        {
            //「メインスレッド」側で処理を行ってもらうコードを記述
            this.Invoke(new Action(() => {
                //「label1」の表示更新
                label1.Text = i.ToString();
            }));
            //1秒待つ
            System.Threading.Thread.Sleep(1000);
        }
    });
 
    //ここは「非同期」なので「Form1」側の処理は止まりません。
 
    //「タスク」内の処理終了を待たずに、次の行へ処理が移ります。「非同期」
    var task2 = Task.Run(() =>
    {
        //↓ここは「同期」処理となり「task2」内「ワーカースレッド」の処理は止まるが、
        //「タスク」内の為、「Form1」自体の処理は止まらない。
        task1.Wait();
        MessageBox.Show("処理終了");
    });
 
    //「task1」「task2」の処理終了を待たずに、
    //この行まで処理が下りてきます。
    //結果「非同期」となります。「Form1」側の処理は止まりません。
}
「0→1→2→3→4→5」「処理終了」
と表示されます。


「async/await」で「Task.Result」使用時の注意
「Task.Result」の処理内には「Task.Wait()」が入っています。
「Task.Result」を使う時には必ず「Task」に対して、
「await」をしておいた方が良いです。
「async/await」で「Invoke」と「Task.Result」使用例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
private async void button1_Click(object sender, EventArgs e)
{
    //「ワーカースレッド」で実行
    var task = Task<string>.Run(() =>
    {
        //「0」~「5」迄カウントアップ
        for (int i = 0; i < 6; i++)
        {
            //「メインスレッド」側で処理を行ってもらうコードを記述
            this.Invoke(new Action(() => {
                //「label1」の表示更新
                label1.Text = i.ToString();
            }));
            //1秒待つ
            System.Threading.Thread.Sleep(1000);
        }
        return "リザルト";
    });
 
    //「await」なので待っている間「Form1」の処理は止まらない。「非同期」
    await task;
    //戻り値取得
    var resultStr = task.Result;
 
    ////↓まとめた書き方でも良い。(通常こちらをつかいます!)
    ////「await」なので戻り値を待つ間「Form1」の処理は止まらない。「非同期」
    //var resultStr = await task;
 
    MessageBox.Show("処理終了:"+resultStr);
}
「0→1→2→3→4→5」「処理終了:リザルト」
と表示されます。



「Task.Wait()」と「例外処理」


「Task.Wait()」と「例外」
「AggregateException」でキャッチできます。
「Task.Wait()」中の「例外」例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private void button1_Click(object sender, EventArgs e)
{
    //「非同期」で実行
    var task = Task.Run(() =>
    {
        System.Threading.Thread.Sleep(1000); // 1秒待つ
        throw new Exception("例外タスク内"); //「例外」発生
    });
 
    //ここは「非同期」なので「Form1」側の処理は止まりません。
 
    try
    {
        //↓ここで「同期的」になり「Form1」側の処理が止まる。
        task.Wait();
    }       
    catch (AggregateException aex) // 「例外」キャッチ
    {
        foreach (var ex in aex.InnerExceptions)
        {
            MessageBox.Show(ex.Message);
        }
    }
    MessageBox.Show("処理終了");
}
「例外タスク内」→「処理終了」
と表示されます。



複数の「Task」をまとめる


複数の「Task」をまとめるには…
「Task.WhenAll()」を使います。
複数の「Task」をまとめる
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
private async void button1_Click(object sender, EventArgs e)
{
    var tasks = new List<Task>(); // TaskをまとめるListを作成
    //「0」~「5」迄カウントアップ
    for(int i=0; i<6; i++)
    {
        var task = MyTaskRun(i); //複数の「Task」を作成
        tasks.Add(task); //TaskをListへ追加
    }        
    var allTask=Task.WhenAll(tasks);//すべてのタスクをまとめる。
 
    //「await」なので待っている間「Form1」の処理は止まらない。「非同期」
    await allTask;
 
    MessageBox.Show("処理終了");
}
 
private Task MyTaskRun(int sec)
{
    return Task.Run(() =>
    {
        System.Threading.Thread.Sleep(sec * 1000); // sec秒待つ
        this.Invoke(new Action(() =>
        {
            label1.Text = sec.ToString();
        }));
    });
}
「0→1→2→3→4→5」「処理終了」
と表示されます。

「Task.WhenAll()」は引数を、
「Task.WhenAll(task1,task2,task3)」のように書く事も可能です。



 複数「Task」の複数「例外処理」


複数「Task」中で起きた複数「例外処理」のキャッチ
「Task.WhenAll()」でタスクをまとめてから、
「Task.Wait()」なら「AggregateException」
「await」なら「Exception」で
複数「Task」内で起きた「例外」をキャッチします。

「Task.Wait()」使用時の複数「例外処理」
それぞれの「Task」で起きた「例外」全てをキャッチする事ができます。
「Task.Wait()」使用時の複数「例外処理」キャッチ例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
private void button1_Click(object sender, EventArgs e)
{
    //「非同期」で実行
    var task1 = Task.Run(() =>
    {
        System.Threading.Thread.Sleep(1000); // 1秒待つ
        throw new Exception("例外タスク1");
    });
    var task2 = Task.Run(() =>
    {
        System.Threading.Thread.Sleep(2000); // 2秒待つ
        throw new Exception("例外タスク2");
    });
    var task3 = Task.Run(() =>
    {
        System.Threading.Thread.Sleep(3000); // 3秒待つ
        throw new Exception("例外タスク3");
    });
    //「Task」をまとめる
    var task = Task.WhenAll(task1, task2, task3);
 
    //ここは「非同期」なので「Form1」側の処理は止まりません。
 
    try
    {
        //↓ここで「同期的」になり「Form1」側の処理が止まる。
        task.Wait();
    }
    catch (AggregateException aex)
    {
        foreach (var ex in aex.InnerExceptions)
        {
            MessageBox.Show(ex.Message);
        }
    }
    MessageBox.Show("処理終了");
}
例外が「AggregateException」でキャッチされ、
「例外タスク1」→「例外タスク2」→「例外タスク3」→「処理終了」
と表示されます。

「await」使用時の複数「例外処理」
最初に「Task」で起きた「例外」のみをキャッチする事ができます。
「await」使用時の複数「例外処理」キャッチ例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
private async void button1_Click(object sender, EventArgs e)
{
    //「非同期」で実行
    var task1 = Task.Run(() =>
    {
        System.Threading.Thread.Sleep(1000); // 1秒待つ
        throw new Exception("例外タスク1");
    });
    var task2 = Task.Run(() =>
    {
        System.Threading.Thread.Sleep(2000); // 2秒待つ
        throw new Exception("例外タスク2");
    });
    var task3 = Task.Run(() =>
    {
        System.Threading.Thread.Sleep(3000); // 3秒待つ
        throw new Exception("例外タスク3");
    });
 
    try
    {
        //「Task」をまとめる
        //「await」なので待っている間「Form1」の処理は止まらない。「非同期」
        await Task.WhenAll(task1, task2, task3);
    }
    // 「await」では「例外」キャッチは最初の1つのみとなる
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
    MessageBox.Show("処理終了");
}
例外が「Exception」でキャッチされ、
「例外タスク1」→「処理終了」
と表示されます。


「BeginInvoke」と「EndInvoke」


「Invoke」と「BeginInvoke」の違い…
「Invoke」は「同期」(呼び出し元の処理は停止して待機)(SendMessage的)
「BeginInvoke」は「非同期」(呼び出し元の処理は継続)(PostMessage的)

「タスク」内「Invoke」。「同期」処理。
「Invoke」内の処理が終了するのを待ち、処理が終了したら次の行へ処理を移します。
「タスク」内「Invoke」。「同期」
//「メインスレッド」側で処理を行ってもらうコードを記述。「同期」
 this.Invoke(new Action(() => {
     //「label1」の表示更新
     label1.Text = i.ToString();
 }));

「タスク」内「BeginInvoke」。「非同期」処理。
「BeginInvoke」内の処理終了を待たずに、次の行へ処理が移ります。
「タスク」内「BeginInvoke」。「非同期」
//「メインスレッド」側で処理を行ってもらうコードを記述。「非同期」
 this.BeginInvoke(new Action(() => {
     //「label1」の表示更新
     label1.Text = i.ToString();
 }));


その他タスク的「BeginInvoke」「EndInvoke」…
タスク的な「BeginInvoke」と「EndInvoke」の使い方は、
現在「Task」クラスで代用する為、ほぼ使われていません。
概要程度でOK。

「BeginInvoke」のタスク的使用例は以下の形になります。
「BeginInvoke」内で「Invoke」を使う事も可能。
「BeginInvoke」の使い方例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private void button1_Click(object sender, EventArgs e)
{
    //「非同期処理」本体
    var asyncWork = new Action(()=> {
        for (int i=0; i<6; i++) {
        //「Invoke」も使える。
        this.Invoke(new Action(()=>{
                label1.Text = i.ToString();
            }));
        System.Threading.Thread.Sleep(1000);
        }
    });
 
    //「非同期処理」終了後に処理
    var endAsyncCallback = new Action<IAsyncResult>((ar) => {
        MessageBox.Show("非同期処理終了");
    });
 
    //「非同期処理」開始。コールバック付き。
    // 処理の終了を待たずに次の行へ処理が進みます。
    asyncWork.BeginInvoke(new AsyncCallback(endAsyncCallback), null);
 
    //「非同期」なので「Form1」側の処理は止まりません。
}
「0→1→2→3→4→5」「非同期処理終了」
と表示されます。


「EndInvoke」を使うと「BeginInvoke」処理のコールバックを
「メインスレッド」側で待つ事ができます。
待っている間は「同期処理」となります。(「Task.Wait()」と同じ)
「ワーカースレッド」に対して「引数」と「戻り値」を付ける場合の
記述もしておきます。
「EndInvoke」、「引数」、「戻り値」を使った処理例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
private void button1_Click(object sender, EventArgs e)
{
    //「非同期処理」本体(戻り値付き、引数付き)
    var asyncWork = new Func<string,string> ((addText)=> {
        for (int i=0; i<6; i++) {
            ////「Invoke」を使うと「デットロック」する為、使用しない。
            //this.Invoke(new Action(()=>{
            //        label1.Text = i.ToString()+addText;
            //    }));
            Console.WriteLine(i.ToString()+addText);
            System.Threading.Thread.Sleep(1000);
        }
        return "戻り値";
    });
 
    //「非同期処理」開始。引数付き。コールバック無し(null)。
    // 処理の終了を待たずに次の行へ処理が進みます。
    var returnAr = asyncWork.BeginInvoke("秒経過", null, null);
 
    //ここは「非同期」なので「Form1」側の処理は止まりません。
 
    //↓ここで「同期処理」的になり「Form1」側の処理が止まる。
    //「ワーカースレッド」の処理が終わるまで、
    //「メインスレッド」の処理を止めて待機
    //(「Task.Wait()」と同じ。「Form1」が動かなくなる。)
    //戻り値の取得
    var resultStr=asyncWork.EndInvoke(returnAr);
 
    MessageBox.Show("処理終了:"+resultStr);
}
「0→1→2→3→4→5」「処理終了:戻り値」
と表示されます。

「7行目」、
コメントにしている「Invoke」の「デットロック」を避けるには、
「27行目」以降の処理を「タスク」に内包して、
「Form1」の動作を止めないようにする。
等の方法があります。




ライブラリを作る場合、
自作のメソッドで「Task.Run()」は使用しない。
(「ConfigureAwait(false)」についての解説)
http://qiita.com/chocolamint/items/ed4999cccf011653cb78
http://qwerty2501.hatenablog.com/entry/2014/04/24/235849




C# 今時のマルチスレッド、非同期のやり方

最初に…


マルチスレッド(非同期処理)は
主にバックエンド処理を行う間、
フロントエンドUIの処理をフリーズさせない為に使います。

例えば、「Form」上の「ボタン」を押すと
なんらかの(バックエンド)処理が行われるソフトを作るとします。

その処理が行われている間、「Form」(フロントエンド)は
操作を受け付けなくなってしまいます。
(「Form」の移動等も出来ない。表示の更新も行われない。)

これを解決できるのがマルチスレッド(非同期処理)。
複数のスレッド処理を平行して行う事ができる為、
「Form」から他のスレッド処理を実行中に
「Form」自身の操作が可能。
「Form」自身の表示も正しく更新されます。

マルチスレッド(非同期処理)は
「Task」(lock)(Invoke)と
「async/await」(SemaphoreSlim)と
「例外処理」が使えればとりあえずOKみたいです。
使い方と周辺知識を自分なりにまとめてみました。
「Task.Wait()」については補足の方に書きました。

C# マルチスレッド、非同期の補足
http://1studying.blogspot.com/2017/04/c_22.html
に補足情報が書いてあります。


準備


「新しいプロジェクトを作成」

「Windowsフォームアプリケーション」

「Form1」へ
「ツールボックス」から
「Label」と「Button」を配置


ボタンをダブルクリックして、
ボタンのクリックイベントを作成しておきます。
1
2
3
4
private void button1_Click(object sender, EventArgs e)
{
 
}


やりたい事


「button1」を押すと、
「label1」の表示が「0」〜「5」迄1秒毎にカウントアップ。
その間、「Form1」の動作が止まらないように処理させたいのです。

「Form1」の動作を止めない為には、
「label1」を「5」迄カウントアップ表示させるプログラムを
「子スレッド」で処理させる必要があります。
この時、「子スレッド」の処理が終わるまでの間、
「Form1」側の動作を止めて待つのが「同期処理」
「Form1」側の動作は止めずに待つのが「非同期処理」です。

「delegate(匿名メソッド、無名メソッド、関数)」と
「定義済みデリゲート」の「Action」「Func」型を使います。
これが分からない場合はリンク先を参照しておいて下さい。
http://1studying.blogspot.jp/2017/04/c-delegateactionfunc.html

ここでは説明上、「Task」内の処理を「子スレッド」とします。
本来は「親スレッド」を「メインスレッド」、
「子スレッド」を「ワーカースレッド」と言うのが正しいです。
(「Task」を使う側は気にする必要はない事なのですが、
「Task」は裏側で「Thread」や「ThreadPool」という処理を
状況により使い分けて作業を行っています。
その為、裏で1つのスレッドを複数「Task」で使い回す事があります。)


「Task」と「Invoke」



「Task」について…
「子スレッド」で処理を行う為に「Task」を使用します。
3つの使い方がありますが、
通常は「①」しか使わないので「①」だけ覚えておけばOKです。
① var task1 = Task.Run( デリゲート );
② var task2 = Task.Factory.StartNew( デリゲート );
③ var task3 = Task( デリゲート ); task3.Start();
普段「①」の「デリゲート」の部分に「子スレッド」で処理させたい内容を書きます。
もし長時間の「子スレッド」実行が必要な場合は、
オプションの指定が可能な「②」を使う事もありますがほぼ使いません。
(「③」はStart()を行うまで「子スレッド」が実行されないタスクです。
「②Factory.StartNew」は「①Ran」と同じ物と思って差し支えありません。
①と②は「子スレッド」が即時に実行されます。
「.NET4.0」時代まで「②」が使われていたが、「.NET4.5」になり、
よりシンプルな「①」の方法が追加されました。)

「Invoke」について…
「インボーク」は「呼び出し」と言う意味です。
「Form1」から「Task」により、「子スレッド」が実行されたとします。
この「子スレッド」から「親スレッドのForm1」のデータを書き換える際、
「Invoke」が使われます。
(複数のスレッド間ではデータのやりとりが制限されている為、
この方法が使われます。「スレッドセーフな呼び出し」等と呼ばれます。)
「Invoke」は「子スレッド」内から
「親スレッドのForm1」に対して、処理を間借りさせてもらい、
「親スレッドのForm1」側の内部でコードを実行させる「メソッド」です。
(「親スレッドのForm1」で実行させたいコードは、
「子スレッド」内の「Invoke」の引数として、デリゲートを使い記述します。)

「Invoke」は「Form」配下の全てのコントロールで使用できます。
(「System.Windows.Formes名前空間」Controlクラスが
フォーム自身と配置される全てのコントロール(アイテム)に継承されている為です。
これにより、
  「System.Windows.Formes.Control.Invoke(Delegate)」
の「メソッド」が継承され使用可能となる。)

処理の例…
これらを踏まえた処理が以下の形となります。
TaskとInvokeを使用した処理例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private void button1_Click(object sender, EventArgs e)
{
    //「子スレッド」で実行
    //コードはデリゲートで記述
    var task = Task.Run(() =>
    {
        //「0」~「5」迄カウントアップ
        for(int i=0; i<6; i++)
        {
            //「親スレッド」側で処理を行ってもらうコードを
            //デリゲートで記述
            this.Invoke(new Action(()=> {
                //「label1」の表示更新
                label1.Text = i.ToString();
            }));
            //1秒待つ
            System.Threading.Thread.Sleep(1000);
        }
    });
    //「子スレッド(カウントアップ)」の処理終了は待ちません
    //ここから先の処理はすぐに実行されます。
}
「button1」をクリックすると「子スレッド」側から、
「0→1→2→3→4→5」と「親スレッドForm1」のラベル表示を更新します。
ラベル表示更新の最中に「Form1」側の動作は止まりません。
つまり「TaskとInvoke」を使用した「非同期処理」となっています。

「Invoke」の戻り値について…
「Invoke」の戻り値はobject型です。
もし上記処理で戻り値を得たい場合は「Invoke」部分の記述が以下の形になります。
Invokeで戻り値を得たい場合
var returnObj=this.Invoke(new Func<object>(() => {
    //「label1」の表示更新
    label1.Text = i.ToString();
    return (object)12341;
}));
「Invoke」は「子スレッド内で親スレッドの処理」を行います。
つまり「Invoke」で「親スレッド」の処理を行った際の戻り値を使用すれば
「子スレッド側」から「親スレッド(Form1)」の情報、
例えば「label1」のプロパティの値などが取得可能です。

「Task」内に処理を全て書く必要性について…
この後説明する「async/await」の所でこの事が
とても重要になります。

上記「TaskとInvokeを使用した処理例」のコードを再確認してみて下さい。
「Task」内の処理(ラベルのカウントアップ処理)が「子スレッド」側で実行中の間、
「親スレッドのForm1」側は「Task」が記述された次の行へ、
すぐに処理を移してしまいます。
「Task」内の処理終了を待ってはくれません。
「非同期処理」にしたいのは
「子スレッド」側で「Task」内の処理(カウントアップ処理)を行っている間、
「親スレッド」側の「Form1」の動作を止めたくないからでしたね。
その為には、
「子スレッド」で行う予定の全処理を「Task」内にまとめて記述する必要があるのです。

ただし、後述する「async/await」を使うと、
「Task」内に全ての処理をまとめる必要がなくなります。
「async/await」はとても便利な機能です。


「Task」と「invokerequired」



「InvokeRequired」について…
「InvokeRequired」プロパティは
自分は現在「子スレッド」側として実行中なのか?を教えてくれます。

例えば、
自分が「親スレッド(Form1と同スレッド)」であれば「Invoke」は必要無いので
直接「Label1」プロパティなどを操作すれば良いです。
しかし、
自分が「子スレッド」の場合「Invoke」を使用して(介して)
「親スレッド(Form1)」内の「Label1」プロパティなどを操作する必要があります。

つまり「InvokeRequired」プロパティは
「Form1」内コントロールの操作に「Invoke」が必要かどうかを判別する為のものです。
「Invoke」が必要な状態であればtrueを返します。
InvokeRequiredを使用した処理例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
private void button1_Click(object sender, EventArgs e)
{
    //「子スレッド」で実行
    var task = Task.Run(() =>
    {
        //「0」~「5」迄カウントアップ
        for(int i=0; i<6; i++)
        {
            //「label1」の表示更新
            SetLabelStr(i.ToString());
            //1秒待つ
            System.Threading.Thread.Sleep(1000);
        }
    });
    //ここから先の処理は
    //「子スレッド」の処理終了を待たずに即実行されます
    //「親スレッド側」であるここからでも「SetLabelStrメソッド」を使う事ができます。
}
 
//「label1」の表示更新(親スレッド側と子スレッド側の両方の呼び出しに対応)
private void SetLabelStr(string text)
{
    //「子スレッド」で実行中かどうか
    //(「Invoke」が必要かどうか)
    if (this.label1.InvokeRequired)
    {
        //「親スレッド」側で処理を行ってもらうコードを
        //デリゲートで記述
        this.Invoke(new Action(() => {
            label1.Text = text;
        }));
        return;
    }
    else
    {
        this.label1.Text = text;
    }
}
「SetLabelStr」メソッド内で「InvokeRequired」を使用して、
「lavel1」への文字列表示更新処理を振り分ける形にする事で、
「SetLabelStr」メソッドがクラス内のどこからでも使える形となります。
自分が「Task」内かを意識せずに「SetLabelStr」メソッドが使えることで
扱いやすいメソッドと言えます。


「Task」と「lock」



「lock」について…
上述の「InvokeRequiredを使用した処理例」のコードを実行して、
「button1」をゆっくり2回クリックすると、
  「0→1→2→3→4→5」「0→1→2→3→4→5」
と「直列的」に処理されて表示されるのではなく、
  「0→1→0→2→1→3→2→4→3→5→45
と表示されます。
これは、2回のクリックにより実行される2つの「子スレッド」が、
並列的」に処理される為です。

1回目のクリックで「子スレッド」が実行、動作中の間は、
2回目以降のクリックによる「子スレッド」の動作を順番待ちさせておくのが
「lock」です。
(動作中の「子スレッド」処理が終了すると、
順番待ちしている他の「子スレッド」が順番に動作中になります。)
これにより「直列的」に処理されているように動作します。
lockを使用した処理例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
//「lock」用オブジェクト
object lockObj = new object();
 
private void button1_Click(object sender, EventArgs e)
{
    //「子スレッド」で実行
    var task = Task.Run(() =>
    {
        //「lock」を使い「子スレッド」処理を順番待ちさせる。
        lock (lockObj)
        {
            //「0」~「5」迄カウントアップ
            for (int i=0; i<6; i++)
            {
                //「label1」の表示更新
                SetLabelStr(i.ToString());
                //1秒待つ
                System.Threading.Thread.Sleep(1000);
            }
 
        }
    });
}
 
//「label1」の表示更新
private void SetLabelStr(string text)
{
    //「子スレッド」で実行中かどうか
    //(「Invoke」が必要かどうか)
    if (this.label1.InvokeRequired)
    {
        //「親スレッド」側で処理を行ってもらうコードを
        //デリゲートで記述
        this.Invoke(new Action(() => {
            label1.Text = text;
        }));
        return;
    }
    else
    {
        this.label1.Text = text;
    }
}
これで「button1」をゆっくり2回クリックした場合、
「0→1→2→3→4→5」「0→1→2→3→4→5」
という「直列的」表示になります。

「lock」を使ってはいけない場面…
「lock」内に「await」は使えません。
「await」内に「lock」も使えません。
「await」中のロックは「semaphoreSlim」を使います。
「await」と「semaphoreSlim」については後述します。


「async/await」



「async/await」について…前説
「async/await」は「Task」をとても簡単に書く方法です。

普通だと「親スレッドのForm1」のコードの中で
「子スレッド」が「Task」内の処理を行っている間、
「親スレッドのForm1」側は「Task」が記述された次の行へ、
すぐに処理を移してしまいます。
「Task」内の処理終了を待ってはくれません。

「await」を使うと、
「親スレッドのForm1」の動作自体は止めないまま、
「Task」内の処理が終了するまで
「await」が記述された次の行への処理を待機させる事ができます。
「await」は裏側で以下のような動作をしています。
1
2
3
非同期で「Task」を使用して「子スレッド」を呼ぶ。
この時の「Task」の行位置を記録し、そのまま「親スレッドのForm1」自体の通常動作処理へ移行。
「Task」内の処理が終了したら「子スレッド」から、記録した行位置まで制御を戻し処理を再開。
「親スレッド」の動作を止めないで、
「子スレッド」からのコールバックを待つ。
みたいな動作になります。

「await」を使う事により、
前述の「InvokeRequiredを使用した処理例」コード内の
「SetLabelStr」メソッドのような書き方が必要なくなります。
(前述ではコントロールを「親スレッド」と「子スレッド」から
同じようにアクセスする為には
「SetLabelStr」メソッドのような書き方が必要だった)

コードの記述内で、
「async」修飾子が付いたメソッドを「非同期メソッド」と呼びます。
これは、
  「async」付きメソッド内配下には
  非同期処理を使用した「await」を
  1つ以上使用していますよ。
という「印」となるものです。


「async/await」について…実践
「非同期メソッド」の処理例は以下の形になります。
「async/await」使用例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//「async」修飾子が付くと処理配下で「await」を1回以上使用された印。
//その為このメソッドは「非同期メソッド」となる。
private async void button1_Click(object sender, EventArgs e)
{
    //「0」~「5」迄カウントアップ
    for (int i = 0; i < 6; i++)
    {
        //「label1」の表示更新
        label1.Text = i.ToString();
        //「子スレッド」処理終了まで「await」で待機。「非同期」
        await Task.Run(() =>
        {
            //1秒待つ(実際では重い処理だったりする部分)
            System.Threading.Thread.Sleep(1000);
        });
    }
    MessageBox.Show("処理終了");
}
「button1」をクリックすると、
  「0→1→2→3→4→5」「処理終了」
の順で表示されます。
「await」を使えば
「Form1」の動作を止めてしまいそうな処理が重そうな部分だけを
部分的に「Task(子スレッド)」として実行させる事が出来ます。
「await」の「Task(子スレッド)」で処理が行われているあいだ、
「Form1」の動作は止まらない状態で、
コード次行処理への進行のみが止まった状態となります。
「Task(子スレッド)」の処理が終われば、
「await」の次の行がら処理が再開されます。
スマートですね。

「await」は「子スレッド」の処理終了を待機させる物なので
「子スレッド」の実行開始位置と、「子スレッド」の終了待機の位置を
別の位置に書く事もできます。
以下のような書き方になります。
awaitで子スレッドの処理終了を別の位置で待機
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//「async」修飾子が付き「非同期メソッド」となる。
private async void button1_Click(object sender, EventArgs e)
{
    //「0」~「5」迄カウントアップ
    for (int i = 0; i < 6; i++)
    {
        //「label1」の表示更新
        label1.Text = i.ToString();
 
        //「子スレッド」を実行。
        //この書き方をすると「子スレッド」の処理終了を待たずに、
        //次の行へ処理が移ります。(awaitが無い為)
        //戻り値は「Task」型になります。
        var task1 = Task.Run(() =>
        {
            //1秒待つ
            System.Threading.Thread.Sleep(1000);
        });
 
        //「親スレッド」側の処理をここに書く事もできます。
        //「子スレッド」を無視して「並列的」に処理されます。
 
        //「await」で待機。「非同期」
        //ここで「子スレッド」処理終了まで待機します。
        await task1;
        //「子スレッド」処理が終了た後に、
        //この行から処理が再開します。
    }
}


「Invoke」と「lock」について…
ここでは記述していませんが、
「await」の「Task」内に「Invoke」を使用しても問題ありません。
あと注意として、
ロックは「lock」でなく、「SemaphoreSlim」を使用します。
詳しくは後述します。


「async/await」の戻り値


「async」を付けた「非同期メソッド」は
戻り値の型が
  「void」か「Task」か「Task<T>」か「ValueTask<T>」
のいずれかである必要があります。
(「ValueTask<T>」型はまだあまり使われていない為、今回説明を省きます。)

戻り値が「void」以外の時は「非同期メソッド」名の最後に、
「Async」か「TaskAsync」という語尾を付ける決まりになっています。
強制ではありませんが付けた方が無難です。
ただし、
「UIイベント」( button1_Click等)のメソッド名称は変更不要です。


「async」の戻り値3パターン
メソッド内で「await」や「async」が使われているメソッドの
戻り値パターンを紹介します。

戻り値のパターンにより
  メソッド内で「return」を書く必要の有無
  タスク内の処理状況の把握が可能、不可能
  メソッド名の名称変更が必要、不必要
と違いが出ます。

「UIイベント」のみ「void」の戻り値がゆるされますので、
それについても説明します。


「void」型の戻り値を使う場合(パターン1)
「void」型の戻り値は
「UIイベントメソッド(button1_Clickなど)」でしか使ってはいけません。
  ・「return」は必要ない為、メソッド内に書きません。
  ・「非同期メソッド」自体の処理状態情報を把握できません。
  ・「非同期メソッド」名、変更なし。
「イベントメソッド」の戻り値は基本「void」型です。
その「イベントメソッド(コントロール系のイベント)」を
「async」を付けた「非同期メソッド」に変更する時に限り
「void」型の戻り値が許可されます。

「void」型の戻り値では、「非同期メソッド」の処理状態情報を
「非同期メソッド」を実行した側が知ることができません。
その為、
「Task」の完了も報告されず、例外も知る事ができません。
つまり、「子スレッド」へ処理を投げたら投げっぱなしの形になります。

「イベントメソッド」以外を「非同期メソッド」に変更する場合は
この後紹介する「Task」型か「Task<T>」型の戻り値を使用して下さい。
「async/await」の戻り値「void」型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//「void」型の戻り値が許されるのは、
//特殊な場合のみです。
//例えば、「ボタンクリックイベント」などは、
//「UIイベントメソッド」なので「void」型の戻り値が許されます。
private async void button1_Click(object sender, EventArgs e)
{
  //
  //「button1_Click」イベントに「async」を記述した為、
  //本来ならこのコード内で「await」を一度以上は使用しなければなりません。
  //が、
  //「await」の戻り値は「void」型はダメ。
  //「TaskやTask<t>」型でないといけないので、
  //「パターン2」や「パターン3」を参照して下さい。
 
  //
  //オススメしないのですが、
  //この中に処理を投げっぱなしの
  //「async」「非同期メソッド」を作る事もできます。
  //後述している
  //      「UIイベント」に「async」を書かない方法
  //を参照して下さい。
}</t>
何度も言いますが、「void」型の戻り値が許されるのは「UIイベント」のみです。
その為「await(非同期)」を使用したコードは必然的に、この後紹介する
  「パターン2」や「パターン3」
の記述方法となるはずです。
あまりオススメしないのですが後述する、
  「UIイベント」に「async」を書かない方法(パターン1補足)
のような「UIイベント」の書き方も可能です。
ただし本来(非同期メソッドを内包するメソッド)は、
「UIイベント」のメソッドであっても「async」修飾子を付けるべきです。


「Task」型の戻り値を使う場合(パターン2)
特に「戻り値」が必要ない場合は、「Task」型の戻り値を使用します。
  ・「return」は必要ない為、メソッド内に書きません。
  ・「非同期メソッド」自体の処理状態情報を知る事ができます。
  ・「非同期メソッド」名、語尾に「Async」追加。
戻り値のない自作のメソッドを「非同期メソッド」にする場合は、
必ず「Task」型の戻り値を使います。
「async/await」の戻り値「Task」型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//「async」修飾子が付き「非同期メソッド」となる。
private async void button1_Click(object sender, EventArgs e)
{
    //「0」~「5」迄カウントアップ
    for (int i = 0; i < 6; i++)
    {
        //「label1」の表示更新
        label1.Text = i.ToString();
 
        //「子スレッド」を実行し、処理終了を待たずに次の行へ処理が移ります。
        //戻り値は「Task」型です。
        var task1 = Wait1secAsync();
 
        //〜〜〜
 
        //「子スレッド」処理終了まで「await」で待機。「非同期」
        await task1;
        //「子スレッド」の処理が終了したらこの行から処理再開
    }
}
 
//「async」修飾子が付き「非同期メソッド」となる。
//戻り値「Task」型
private async Task Wait1secAsync()
{
    //1秒待つ
    await Task.Run(() => {
        System.Threading.Thread.Sleep(1000);
    });
}


「Task<T>」型の戻り値を使う場合(パターン3)
「戻り値」が欲しい場合「Task<T>」型の戻り値を使用します。
  ・「return」に戻り値を書きます。
    「return」された型は「Task<T>」型で戻り値で受け取ります。
    (「int」型を「return」する場合、「Task<int>」型で受け取る。)
  ・「非同期メソッド」自体の処理状態情報を知る事ができます。
  ・「非同期メソッド」名、語尾に「Async」追加。
戻り値のある自作メソッドを「非同期メソッド」にする場合は、
必ず「Task<T>」型の戻り値にします。
「await」で「子スレッド」の処理が終了するのを待機してから
「Task<T>.Result」を使い、受け取った戻り値の取り出しを行います。
(「Task<T>.Result」には「Task.Wait()」処理が内包されています。
使用前には「await」を使って「子スレッド」の処理終了を待つ事を
忘れないように注意して下さい。)
この例では、文字列処理をあえて「子スレッド」側で行います。
「async/await」の戻り値「Task<T>」型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//「async」修飾子が付き「非同期メソッド」となる。
private async void button1_Click(object sender, EventArgs e)
{
    var addStr = "";
 
    //「0」~「5」迄カウントアップ
    for (int i = 0; i < 6; i++)
    {
        //「label1」の表示更新
        label1.Text = i.ToString()+addStr;
 
        //「子スレッド」を実行し、処理終了を待たずに
        //次の行へ処理が移ります。
        //戻り値は「Task<T>」(ここでは「Task<string>」)型です。
        var task1 = WaitSecAsync(1000);
 
        //~~~
 
        //「子スレッド」処理終了まで「await」で待機。「非同期」
        await task1;
 
        //「子スレッド」の戻り値から結果を得る
        addStr = task1.Result;
    }
}
 
//「async」修飾子が付き「非同期メソッド」となる。
//戻り値「Task<T>」(ここでは「Task<string>」)型
private async Task<string> WaitSecAsync(int num)
{
    //1000分のnum秒待つ
    var sec = await Task.Run(() => {
        System.Threading.Thread.Sleep( num );
        return num/1000; // 秒を返す。secへ格納。
    });
    return "×"+sec.ToString()+"秒経過"; // "×?秒経過"文字列を返す
}
ボタンクリックで、
「0→1×1秒経過→2×1秒経過→3×1秒経過→4×1秒経過→5×1秒経過」
と表示されます。


「UIイベント」に「async」を書かない方法(パターン1補足)
「void」型の戻り値は「UIイベント」のみに許されます。
「ActionやFunc」内でラムダ式に「async」を使う事により
「button1_Clickイベント」メソッドを「非同期メソッド」にしないで、
「button1_Clickイベント」メソッドはそのままに、
「Task」型の戻り値でラムダ式の「非同期メソッド」を内包させる事ができます。
以下の形になります。
ラムダ式で「async」「非同期メソッド」を内包
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private void button1_Click(object sender, EventArgs e)
{
    //ラムダ式で「async」「非同期メソッド」作る。
    Func<Task> func1 = async () =>
    {
        //「0」~「5」迄カウントアップ
        for (int i = 0; i < 6; i++)
        {
            //「label1」の表示更新
            label1.Text = i.ToString();
            //「子スレッド」処理終了まで「await」で待機。「非同期」
            await Task.Run(() =>
            {
                //1秒待つ
                System.Threading.Thread.Sleep(1000);
            });
        }
    };
 
    //作った「非同期メソッド」の実行
    //戻り値「Task」型
    var task0=func1();
}
その場限りの「非同期」のテストを行いたい時などに、
とりあえずこの書き方で動作確認してみる事がよくあります。
ただし本来は、
何か特別な意図がない場合は「UIイベント」のメソッドにも
「async」修飾子を付けるべきで、
必然的に
  「パターン2」や「パターン3」
の記述方法となるハズです。



「async/await」中のロック、

「Task」と「SemaphoreSlim」


「lock」内に「await」は使えません。
「await」内に「lock」も使えません。
(複数の「await」の「子スレッド」を順に実行した時に、
同じ「スレッド」で動く事が保証されていない為です)
「await」中のロックは「semaphoreSlim」を使います。

「セマフォスリム」は、
事前に「セマフォロック」用のオブジェクトを用意しておいて、
「try{ 〜 }finally{ 〜 }」にからめて使用します。
「セマフォスリム」の使用例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//「セマフォロック」用「SemaphoreSlim」オブジェクト
//(コンストラクタ時の引数は並列を許可する「子スレッド」の数)
private System.Threading.SemaphoreSlim _semaphoreLock = new System.Threading.SemaphoreSlim(1);
 
private void button1_Click(object sender, EventArgs e)
{
    //ラムダ式で「async」「非同期メソッド」作る。
    Func<Task> func1 = async () =>
    {
        await _semaphoreLock.WaitAsync(); // 「セマフォロック」
        try
        {
 
            //「0」~「5」迄カウントアップ
            for (int i = 0; i < 6; i++)
            {
                //「label1」の表示更新
                label1.Text = i.ToString();
                //「子スレッド」処理終了まで「await」で待機。「非同期」
                await Task.Run(() =>
                {
                    //1秒待つ
                    System.Threading.Thread.Sleep(1000);
                });
            }
 
        }
        finally
        {
            _semaphoreLock.Release(); // 「セマフォロック」解除
        }
    };
 
    //作った「非同期メソッド」の実行
    //戻り値「Task」型
    var task0 = func1();
}
これで「button1」を素早く2回クリックした場合、
「0→1→2→3→4→5」「0→1→2→3→4→5」
という「直列的」表示になります。



「他のスレッド」内で「例外」発生時の注意


「VisualStudio」の「デバッグモード」で、
「子スレッド」や「子スレッド」内「Invoke」等、
「他のスレッド」内で「例外」が起きた際の挙動に注意して下さい。
例えば、
「VisualStudio」で「他スレッド」内での「例外」例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private async void button1_Click(object sender, EventArgs e)
{
    try
    {
        //「await」で実行。「非同期」
        await Task.Run(() =>
        {
            MessageBox.Show("タスク処理中");
            throw new Exception("例外タスク内"); //「例外」発生
        });
 
        MessageBox.Show("処理");
    }
    catch (Exception ex) // 「例外」キャッチ
    {
        MessageBox.Show(ex.Message);
    }
    MessageBox.Show("処理終了");
}
は、
  『「タスク処理中」→「例外タスク内」→「処理終了」と表示されます。』
と普段は書きますが、
実際の「VisualStudio」上では以下のような表示となります。
↓「タスク処理中」表示

ここで「スレッド」を跨ぐ為、エラー表示。
「ユーザーが処理していない例外」のメッセージ。
(「F5」を押し、「続行」させるだけで大丈夫です。)

↓「例外タスク内」表示

↓「処理終了」表示

この様に、「スレッド」を跨ぐ時に、
「VisualStudio」側のメッセージが介在する事を認識しておいて下さい。

最初から「VisualStudio」上で、
「CTRL+F5」の「デバッグ無しで開始」で実行を開始する事で、
「VisualStudio」側のメッセージの介在を防ぐ方法も有ります。



「async/await」と「例外処理」


「Task」内の例外を拾うには…
「await」「Task.wait()」「Task.result」を
「try〜catch」で囲む事で「Task」内の「例外」を掴めます。
(「await」以外の「例外処理」については補足で説明します。)

ここでは「await」での「例外処理」について説明します。
「Task.Run()」その物を「try〜catch」で囲んでも「例外」を
拾えない事に注意して下さい。


「await」中の「例外」
「Exception」でキャッチできます。
「await」中の「例外」例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private async void button1_Click(object sender, EventArgs e)
{
    //「子スレッド」で実行
    var task = Task.Run(() =>
    {
        this.Invoke(new Action(() =>
        {
            label1.Text = "abcde";
        }));
        System.Threading.Thread.Sleep(1000); // 1秒待つ
        throw new Exception("例外タスク内"); //「例外」発生
    });
 
    try
    {
        //「await」なので待っている間「Form1」の処理は止まらない。「非同期」
        await task;
    }       
    catch (Exception ex) // 「例外」キャッチ
    {
        MessageBox.Show(ex.Message);
    }
    MessageBox.Show("処理終了");
}
「abcde」→「例外タスク内」→「処理終了」
と表示されます。


「await」中「Invoke」内の「例外」
「Exception」でキャッチできます。
「Invoke」内の「例外」を「await」内でキャッチ。
「await」内の「例外」を「親スレッド」内でキャッチしています。
「await」内で「例外」を再スローする際、
「throw;」は「throw ex;」としていない所にも注意して下さい。
(Javaの場合の「throw ex;」とC#の「throw;」が同義となります。
「throw;」→throwされる例外「StackTrace」を保持。
「throw ex;」→例外「StackTrace」をその時点から更新。)
「await」中「Invoke」内の「例外」例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
private async void button1_Click(object sender, EventArgs e)
{
    //「子スレッド」で実行
    var task = Task.Run(() =>
    {
        try
        {
            this.Invoke(new Action(() =>
            {
                throw new Exception("例外Invoke内"); //「例外」発生
                label1.Text = "abcde";
            }));
            System.Threading.Thread.Sleep(1000); // 1秒待つ
        }
        catch (Exception ex) // 「例外」キャッチ
        {
            MessageBox.Show(ex.Message);
            throw// 再スロー
        }
    });
 
    try
    {
        //「await」なので待っている間「Form1」の処理は止まらない。「非同期」
        await task;
    }       
    catch (Exception ex) // 「例外」キャッチ
    {
        MessageBox.Show(ex.Message);
    }
    MessageBox.Show("処理終了");
}
「例外Invoke内」→「例外Invoke内」→「処理終了」
と表示されます。

「上位スレッド」のみで「例外」をキャッチしたい場合は、
「Task」内の「Try{ }catch(Exception ex){ 〜 }」そのものを削除してもかまいません。
「Task」内の「例外処理」を残すのであれば、
try
{
  〜
}
catch
{
  throw; // 再スロー
}
の様に「(Exception ex)」を省略して再スローさせても、
「上位スレッド」で「例外」をキャッチできます。


「Task.Wait()」と「例外」
「AggregateException」でキャッチできます。
「Task.Wait()」についてはここでは説明しません。
補足「Task.Wait()」と「例外処理」の方で説明しています。




C# マルチスレッド、非同期の補足
http://1studying.blogspot.com/2017/04/c_22.html
に補足情報を書いておきました。

「Task.Wait()」は「Task」の処理終了を待つ間、
「親スレッド」の動作を止めてしまいます。
「Invoke」や「await」との組み合わせでデットロックが紛れやすいです。
補足の方で説明しています。

「Task.Wait()」の「例外」についても
補足の方で説明しています。

以下サイトを参考にしました。
http://gomocool.net/gomokulog/?p=762
http://kimux.net/?p=902
http://qiita.com/acple@github/items/8f63aacb13de9954c5da
http://blog.xin9le.net/entry/2012/07/30/123150
https://ufcpp.wordpress.com/2012/11/12/asyncawait%E3%81%A8%E5%90%8C%E6%99%82%E5%AE%9F%E8%A1%8C%E5%88%B6%E5%BE%A1/
http://blog.shtair.net/2014/07/29/%E9%9D%9E%E5%90%8C%E6%9C%9F%E3%81%AE%E5%90%8C%E6%9C%9F/

非同期のパターン
http://qiita.com/0xfffffff7/items/f7ed068350ed420e5219


独学なので、認識違いがあったらごめんなさい。
以上。



2017年4月11日火曜日

C# delegate(匿名メソッド、無名メソッド)Action、Func

c#のデリゲートについて、一度整理。

デリゲート。意味は「代理人」

「delegate」は色々な呼び方がある。
→匿名メソッド、無名メソッド、匿名関数。

「delegate」


使用例を見てみる。
delegateの使い方
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//匿名メソッドの「型」(引数と戻り値)の宣言のみ
//TestMethod「型」がdelegateで宣言されている
public delegate void TestMethod(string s);
 
//ボタンクリック時イベント
private void button1_Click(object sender, EventArgs e)
{
    //delegateで宣言してある「TestMehod型」の変数に
    //匿名メソッドの処理をdelegateで書いて格納する
    TestMethod test1 = delegate (string msg)
    {
        MessageBox.Show(msg+"1");
    };
 
    //ラムダ式バージョン(処理内容は同じ)
    TestMethod test2 = (msg) => {
        MessageBox.Show(msg+"2");
    };
 
    //個々のメソッドとして使用できる。
    test1("メッセージ"); //「メッセージ1」と表示
    test2("メッセージ"); //「メッセージ2」と表示
 
}
要するに、変数をメソッドとして扱いたいのである。

 匿名メソッドを作る為の型を作る必要があり、
「引数と戻り値の数」だけを宣言する為に「delegate」が使われている。
(c++だと関数ポインタ、std::function)


「Action」「Func」


「Action」と「Func」は「定義済みデリゲート」。
「delegate」の宣言が「Action」と「Func」で既に宣言してある為、
「delegate」の宣言が必要ない。
その為、当然使いやすい。
「Action」と「Func」の使い方
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//「Action」は 「戻り値無し」
//「Func」は 「戻り値有り」
//引数や戻り値の型を<>内に指定。
 
//引数:void
//戻り値:void → 「Acton」を使う
Action act1 = () =>
{
  MessageBox.Show("a");
};
 
//引数:string
//戻り値:void → 「Acton」を使う
Action<string> act2 = (valueStr) =>
{
  MessageBox.Show(valueStr);
};
 
//引数:int,int
//戻り値:string → 「Func」を使う
Func<int, int, string> funcAdd = (x, y) =>
{
  return (x + y).ToString();
};
 
//処理結果
act1(); //「a」
act2("abc"); //「abc」
MessageBox.Show(funcAdd(1,2)); //「3」

デリゲートはその場限りのメソッドを書く事が出来る。
これを「定義済みデリゲート」を使って
「Action(戻り値無し、引数16個まで)」と
「Func(戻り値有り、引数16個まで)」を使う事により、
「delegate」の宣言がいらなくなり、
ラムダ式で書けば、かなりスマートに書く事が出来る。


ラムダ式で書いた匿名メソッド(delegate)の受け皿に
delegate型(「Action」「Func」型)の変数を使うイメージとなる。

delegate型の変数はメソッドとして使用できる。


他には「マルチキャストデリゲート」というのがある。
1つのデリゲート変数に、複数の無名メソッドを代入する事ができる。
これは、代入した無名メソッドの呼び出し順が保証されていない為、
あまり使わない方がよさそう…。

「MethodInvoker」の説明はしませんでした。
「Action」と「MethodInvoker」は同じ物です。
「Action」を利用しましょう。


以下サイトを参考にしました。
http://9cubed.info/article/delegate/

2017年4月7日金曜日

Bloggerでソースコードを貼り付ける(改改)

最初に…


色々な物を試して、
SyntaxHighlighter

google code prettify

ex code prettify

highlight.js

SyntaxHighlighter
のようにたどって渡り歩いた結果、
結局「SyntaxHighlighter」に戻ってきました。
戻ってきた理由が、
「WordPress」の影響もあってか、
一番使われているからです。
各種言語対応もされています。

使うにあたって、
設定やCDN先を見直しました。
styleのカスタマイズもだいぶこだわりました。
そこそこの見栄えになったと思います。
(その分ちょっとコードが長くなりました。)
読み込みが早くなるように調整しました。
変な挙動も起こらないように調整しました。
ちゃんと問題無く使えそうです。

javascript
1
2
3
4
5
6
7
8
//こんな感じのコード表示になります。
function test(){
  for (var i=0; i < 10; i++){
    var value = 1234567;
    var str = "abcde";
    alert("test");
  }
}
1
2
タイトル帯無しで
コード表示
タイトル
行番号非表示で
コード表示
タイトル帯無しで
行番号非表示で
コード表示

使う為の準備



まず
「テーマ」を選択してから「HTMLの編集」を選択


次に、
これ↓をコピーして下さい。
(注意:コードをマウスでダブルクリックしないで、
複数行ドラッグでコードを全選択してコピーした方が結果が良いみたいです)
下のコードを選択後、コピーします。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
<!-- SyntaxHighlighter 追加 -->
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/SyntaxHighlighter/3.0.83/scripts/shCore.js"/>
<!-- autoloader対応 -->
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/SyntaxHighlighter/3.0.83/scripts/shAutoloader.js"/>
<!-- /HTMLと他言語を同時にハイライト対応用(html-script: true) -->
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/SyntaxHighlighter/3.0.83/scripts/shBrushXml.js"/>
<!-- テーマの読み込み(shCore.css内包版) -->
<link type="text/css" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/SyntaxHighlighter/3.0.83/styles/shCoreFadeToGrey.css" />
<script language='javascript' type='text/javascript'>
var shCdnUrlStr='https://cdnjs.cloudflare.com/ajax/libs/SyntaxHighlighter/3.0.83';
SyntaxHighlighter.autoloader(
'actionscript3 as3      '+shCdnUrlStr+'/scripts/shBrushAS3.js',
'bash shell             '+shCdnUrlStr+'/scripts/shBrushBash.js',
'coldfusion cf          '+shCdnUrlStr+'/scripts/shBrushColdFusion.js',
'cpp c                  '+shCdnUrlStr+'/scripts/shBrushCpp.js',
'c# c-sharp csharp      '+shCdnUrlStr+'/scripts/shBrushCSharp.js',
'css                    '+shCdnUrlStr+'/scripts/shBrushCss.js',
'delphi pascal pas      '+shCdnUrlStr+'/scripts/shBrushDelphi.js',
'diff patch             '+shCdnUrlStr+'/scripts/shBrushDiff.js',
'erlang erl             '+shCdnUrlStr+'/scripts/shBrushErlang.js',
'groovy                 '+shCdnUrlStr+'/scripts/shBrushGroovy.js',
'html xml xhtml xslt    '+shCdnUrlStr+'/scripts/shBrushXml.js',
'java                   '+shCdnUrlStr+'/scripts/shBrushJava.js',
'javafx jfx             '+shCdnUrlStr+'/scripts/shBrushJavaFX.js',
'javascript js jscript  '+shCdnUrlStr+'/scripts/shBrushJScript.js',
'perl pl                '+shCdnUrlStr+'/scripts/shBrushPerl.js',
'php                    '+shCdnUrlStr+'/scripts/shBrushPhp.js',
'text plain             '+shCdnUrlStr+'/scripts/shBrushPlain.js',
'powershell ps          '+shCdnUrlStr+'/scripts/shBrushPlain.js',
'python py              '+shCdnUrlStr+'/scripts/shBrushPython.js',
'ruby rails ror         '+shCdnUrlStr+'/scripts/shBrushRuby.js',
'scala                  '+shCdnUrlStr+'/scripts/shBrushScala.js',
'sql                    '+shCdnUrlStr+'/scripts/shBrushSql.js',
'vb vbnet               '+shCdnUrlStr+'/scripts/shBrushVb.js'
);
SyntaxHighlighter.config.bloggerMode = true; // Blogger対応
SyntaxHighlighter.defaults['toolbar'] = false; // 「?」表示
SyntaxHighlighter.defaults['auto-links'] = false; // 自動リンク
//不具合が起きる為コメント化。使用時はpre側で要記述。
//SyntaxHighlighter.defaults['html-script'] = true; //HTMLと他言語を同時にハイライト
SyntaxHighlighter.defaults['tab-size'] = 2; //tabインデント量
// コード表示させるタグ名(デフォルト"pre")
// SyntaxHighlighter.config.tagName="";
SyntaxHighlighter.all();
</script>
<style>
.syntaxhighlighter {
  font-size: 14px !important; /* フォントサイズ */
  margin: 0.5em 0em 0.5em 0em !important; /* 上下空き */
  border: 1px solid #bbbbbb !important; /* 罫巻 */
  /* 角丸 */
  -webkit-border-radius: 10px !important;
  -moz-border-radius: 10px !important;
  -ms-border-radius: 10px !important;
  -o-border-radius: 10px !important;
  border-radius: 10px !important;
}
.syntaxhighlighter table caption {
  padding: 0.3em 0 0.1em 1em !important; /* タイトルpadding */
  color: #ffffee !important; /* タイトル文字色 */
  background-color: #666666 !important; /* タイトル背景色 */
}
.syntaxhighlighter, .syntaxhighlighter div,
.syntaxhighlighter code, .syntaxhighlighter span {
  line-height: 1.2em !important; /* 行間 */
}
.syntaxhighlighter table td.code {
  padding: 0.3em 0 !important; /* コードエリアのpadding */
}
.syntaxhighlighter {
  background-color: #222222 !important; /* コード背景色 */
}
.syntaxhighlighter .line.alt1 {
  background-color: #222222 !important; /* コード偶数行背景色 */
}
.syntaxhighlighter .line.alt2 {
  background-color: #333333 !important; /* コード奇数行背景色 */
}
.syntaxhighlighter .comments, .syntaxhighlighter .comments a{
    color: #88eeee !important; /* コメント色 */
}
.syntaxhighlighter .preprocessor {
  color: #88eeee !important; /* #以降の色 */
}
.syntaxhighlighter .value {
  color: #00cc00 !important; /* 代入数字色 */
}
.syntaxhighlighter .keyword {
  color: #ff0000 !important; /* キーワード色 */
}
.syntaxhighlighter .script {
  font-weight: bold !important;
  color: #ff0000 !important; /* スクリプト色 */
  background-color: none !important;
}
.syntaxhighlighter.nogutter td.code .container textarea,
.syntaxhighlighter.nogutter td.code .line {
  padding-left: 1em !important; /* 行番号非表示時左空き量 */
}
.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 {
  background-color: #114466 !important; /* 行強調時、行背景色 */
}
.syntaxhighlighter .line.highlighted.number {
  color: white !important; /* 行強調時、数字? */
}
.syntaxhighlighter .gutter .line.highlighted {
  background-color: #3185b9 !important; /* 行強調時、行番号背景色 */
  color: #121212 !important; /* 行強調時、行番号数字色 */
}
</style>
<!-- SyntaxHighlighter 追加 -->

そうしたら「</body>」タグを探して、
「</body>」タグの上部へペースト。
(検索をかけるか、下から探します。)
(注:「</head>」タグではありません!)

ペースト後、
「テーマを保存」を押し忘れないようにして下さい。
(保存した物を再度読み込んで表示すると、
ペーストした文字が化けたような表示になりますが、
そういう物なので問題ありません。)

使ってみる



ブログの投稿時の「HTML」編集画面で、
ブログ内に自分のコードを貼り付けたい場所で
どちらかの形でコピー&ペーストして下さい。

コードの表示方法は以下の2パターンです。

「pre」パターン
preタグを使う場合
1
2
3
<pre class="brush:text" title="タイトル">
書きたいコード
</pre>
「script CDATA」パターン
scriptタグを使う場合
1
2
3
<script class="brush:text" title="タイトル" type="syntaxhighlighter"><![CDATA[
書きたいコード
]]></script>

class="brush:[言語]"」の部分が、
以下の言語に対応しています。
−−−−−−−−−−−−−−−
「actionscript3」「as3」/「bash」「shell」/「coldfusion」「cf」/
「cpp」「c」/「c#」「c-sharp」「csharp」/「css」/
「delphi」「pascal」「pas」/「diff」「patch」/「erlang」「erl」/
「groovy」/「html」「xml」「xhtml」「xslt」/「java」/
「javafx」「jfx」/「javascript」「js」「jscript」/「perl」「pl」/
「php」/「text」「plain」/「powershell」「ps」/
「python」「py」/「ruby」「rails」「ror」/「scala」/
「sql」/「vb」「vbnet」
−−−−−−−−−−−−−−−
ハイライトが必要ない時や、
どの言語を使用して良いか分からない時は
「text」を指定すれば良いでしょう。

「pre」パターンの場合、
「<」を「&lt;」へ
「>」を「&gt;」へ
の置換が必要になります。
コードを自動で置換生成させるツールを自作しました。
こちらのサイトで使用出来ます。ブックマークして使用して下さい。
●コード生成ツール(SyntaxHighlighter用)●
http://1studying.blogspot.com/2017/04/blog-post.html

「script CDATA」パターンの場合、
「<」や「>」等の置換は必要ありません。
そのままコードを貼り付ければOKです。
(但し、「<script>」タグと「</script>」タグ、「<br />」タグでは
「<script>」を「&lt;script&gt;」へ
「]]>」を「]]&gt;」へ
「</script>」を「&lt;/script&gt;」へ
「<br />」を「&lt;br /&gt;」へ
の置換が必要となります。)


class="brush:js html-script: true"」等とすると、
HTMLとjavascriptの言語を同時にハイライト可能です。

title=""」のようにタイトル内容を削除すると、
上部のタイトル帯が消えます。

1
2
タイトル帯無しは
こんな感じになります。

注意!
この「SyntaxHighlighter」は
通常「プレビュー」での確認が可能です!
(httpsのcdnを使用している為)
便利です!
但し、コード内の横スクロール操作等は「プレビュー」から行えません。

その場合、一度「公開」してから

ここから「表示」させて確認して下さい。

スマホ対応する



上記方法だけではコードの表示が、
スマートフォン表示に対応していません。
対応させましょう。
「テーマ」でモバイル設定の「歯車」を選択
(使用しているテーマによっては
「歯車」のマークが無い場合があります。
その場合、設定は必要ありません。)

そうしたら、
モバイルテーマの選択で、
「カスタム」を選んで保存して下さい。

これでコードの表示が
スマホに対応します。
(スマホ表示で、
コードの横スクロールは出来ない場合があるようです。)

以上です。



行番号の非表示
行番号の非表示は
「gutter:false」を追加します

貼り付けるコード
1
2
3
4
<pre class="brush:text gutter:false" title="">あいうえお
かきくけこかきくけこ
さしすせそ
</pre>
結果はこんな感じに表示されます

あいうえお
かきくけこかきくけこ
さしすせそ


開始行番号の指定と行の強調
開始行番号の指定は「first-line:10」
任意の行を強調するのは「highlight:[13, 14]」

貼り付けるコード
1
2
3
4
5
<pre class="brush:text first-line:10 highlight:[11, 12]" title="">あいうえお
かきくけこ
さしすせそ
たちつてと
</pre>
結果はこんな感じに表示されます

10
11
12
13
あいうえお
かきくけこ
さしすせそ
たちつてと

設定を変更しやすいように、
各種設定にコメントを入れてありますので、
自分好みになるようにテーマやスタイルを
色々調整してみるのも良いかもしれません。

正直私自身も、もう少し調整を行うかもしれません。
行った場合はこちらのソースを更新します。



以下のサイトを参考にしています。
・「SyntaxHighlighter」公式HP
http://alexgorbatchev.com/SyntaxHighlighter/

「SyntaxHighlighter」のCDNは公式HPでホストされている物を
使用すると「https」でない為、
「blogger」での「プレビュー」表示が出来なくなってしまいます。

その為、
大量のjavascriptライブラリを「https」でCDN配信している
・「cdnjs」
https://cdnjs.com/
でホストされている「SyntaxHighlighter」を使用しています。
https://cdnjs.com/libraries/SyntaxHighlighter
これにより「プレビュー」表示が可能となっています。
(CDNとは、サーバーを分散させ、ネット上でファイルを共有、配信を行う事)


最近「Orange Zelo」というBloggerテーマが気になっています。
ここで紹介されています。
https://www.ura-no-ura.com/2018/08/blogger-theme-zelo-vs-vaster2.html




2017年4月5日水曜日

Bloggerでソースコードを貼り付ける(改)


注意


現在、私自身は「SyntaxHighlighter」の
設定を色々調節した物を使っています。
「Blogger」の「プレビュー」に対応し、
対応言語が多めです。動作も軽めになりました。
↓こちらで紹介しています。オススメです。
●Bloggerでソースコードを貼り付ける(改改)●
http://1studying.blogspot.jp/2017/04/blogger_93.html

最初に…


Google純正の「Google Code Prettify」の
高機能版「jquery.ex-code-prettify」を見つけたので、
これを使う事にした。

色々な物を試して、
SyntaxHighlighter

google code prettify

ex code prettify
のようにたどって、これに行き着きました。
私的には一番しっくりきそうな感じです。


JavaScriptBACK全選択
  1. //こんな感じのコード表示に
  2. //なります。
  3. alert("test");

使う為の準備


まず
「テーマ」を選択してから「HTMLの編集」を選択

次に、
これ↓をコピーして下さい。

  1. <!-- ex-code-prettify 追加 -->
  2. <link href='https://cyokodog.github.io/jquery.ex-code-prettify/jquery.ex-code-prettify.css' media='screen' rel='stylesheet' type='text/css'/>
  3. <script src='https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js'/>
  4. <script src='https://cyokodog.github.io/jquery.ex-code-prettify/google-code-prettify/prettify.js'/>
  5. <script src='https://cyokodog.github.io/jquery.ex-code-prettify/jquery.ex-code-prettify.js'/>
  6. <style type="text/css">
  7. .prettyprint.linenums {
  8. box-shadow: 32px 0 0 #f0f0f0 inset, 36px 0 0 #eee inset;
  9. overflow: auto; /* 横スクロールバーON */
  10. }
  11. pre.ex-code-prettify ol.linenums{
  12. margin-left:0px;
  13. padding-left:40px; /* 行番号領域-8px */
  14. }
  15. pre.ex-code-prettify ol.linenums li{
  16. margin-left:0px;
  17. color: #bebec5;
  18. line-height: 10px; /* 行間-8px */
  19. text-shadow: 0 1px 0 #fff;
  20. }
  21. .ex-code-prettify-contents{
  22. border:solid 1px #aaa; /* 罫巻色変更 */
  23. border-radius:10px; /* 罫巻角丸量+6px */
  24. word-wrap: break-word;
  25. overflow: auto;
  26. }
  27. </style>
  28. <script>
  29. $(function(){
  30. $('.code').exCodePrettify({tabToSpace:true,rawLabel : '全選択'});
  31. });
  32. </script>


そうしたら「</head>」タグを探して、
「</head>」タグの上部へペースト

ペースト後、
「テーマを保存」を押し忘れないようにして下さい。

使ってみる


ブログの投稿時の「HTML」編集画面で、
ブログ内に自分のコードを貼り付けたい場所で
以下の形でコピー&ペーストして下さい。

  1. <textarea class="code" data-ex-code-prettify-param="{codeType:''}"> 
  2. 書きたいコード
  3. </textarea>

「codeType:''」の「''」内に言語の文字を指定します。
ーーーーーーーーーー
「'css'」→「CSS」
「'html'」→「HTML」
「'script'」→「JavaScript」
「'jsFile'」→「JS File」
「'cssFile'」→「CSS File」
ーーーーーーーーーー
「codeType:'html'」等と指定出来ます。
その他の言語の場合は「''」とします。

コードの記述でよくある、
「<」や「>」等の置換は必要ありません。
そのままコードを貼り付ければOKです。
便利ですね。

注意!
ex code prettify」は
「プレビュー」では正しく表示できません。
一度「公開」してから

ここから「表示」させて確認して下さい。

スマホ対応する


上記方法だけではコードの表示が、
スマートフォン表示に対応していません。
対応させましょう。
「テーマ」でモバイル設定の「歯車」を選択
(使用しているテーマによっては
「歯車」のマークが無い場合があります。
その場合、設定は必要ありません。)

そうしたら、
モバイルテーマの選択で、
「カスタム」を選んで保存して下さい。

これでコードの表示が
スマホに対応します。

以上です。



「jQuery」は「Google」にホストされている物を使っています。
「GoogleDevelopers」
https://developers.google.com/products/

「Google Hosted Libraries」
https://developers.google.com/speed/libraries/
でホストされている「jQuery」を使用しています。


「google-code-prettify」は
・「google-code-prettify」GitHub
https://github.com/cyokodog/jquery.ex-code-prettify
のファイルをどこかからホスティングしなくてはいけません。

その為、
「GitHub Pages」という「GitHub」自身が行っているサービスを使って、
「GitHub」のリポジトリから直接ホスティングをする方法を使用しています。
・「GitHub Pages」の使い方説明
http://himaratsu.hatenablog.com/entry/github/hosting
・「GitHub Pages」HP
https://pages.github.com/
・こちらから「GitHub Pages」へ登録されている物を使用しています。
 ウォーズマンが格好良いHPです。
http://www.cyokodog.net/

他の方法として
「RawGit」という所のサービスを使用して、
「GitHub」のリポジトリから直接ホスティングする方法もあります。
「RawGit」は「GitHub」のファイルをCDNとして参照するサービスです。
ホスティング先を変更するのであればこちらの方が簡単だと思います。
(CDNとは、サーバーを分散させ、ネット上でファイルを共有、配信を行う事)
・「RawGit」の使い方説明
http://qiita.com/takanorig/items/89db46120d2ec171e3d8
・「RawGit」HP
http://rawgit.com/


後、一応別のコード記述方法も載せておきます。

  1. <pre class="code" data-ex-code-prettify-param="{codeType:''}">
  2. 書きたいコード
  3. </pre>

この書き方の場合は、
「<」を「&lt;」へ
「>」を「&gt;」へ
の置換が必要になります。
自動で置換させたい場合はこちらのサイトで出来ます。
http://www.kuribo.info/2008/04/code-prettify.html



↑Topへ