特集:VB 10概説

Visual Basic 2010の新機能

尾崎 義尚
2011/05/17
2011/05/18 更新
Page1 Page2 Page3 Page4

並列処理

 VB 2010では、並列処理が以前よりも簡単に書けるようになっている。

 まず始めに、「並列」と「並行」の区別について簡単に説明する。英語でいうと、並列は「Parallel」であり、並行は「Concurrent」となり、明らかに違うものだと分かるのだが、日本語だと「行」と「列」という縦横の概念で表現されているので分かりにくい。概念的なイメージとしては、1つの作業を複数人で実施するのが「並列(Parallel:パラでやる)」であり、複数の作業を1人が同時に実施するのが「並行(マルチタスクが分かりやすい)」である。今回解説する並列処理は、1つの作業を複数で処理する機能であることを念頭に置いてほしい。

 並列処理はご存じのとおり、新しい概念ではない。.NET 1.0からスレッドは存在していたが、使い方が簡単とはいえなかった。いまやほとんどのPCが複数コアのCPUを搭載しており、今後もコア数が増えていくことが予想される。処理を並列化して、複数コアを効率的に使えるようにすることは、今後のプログラミングで重要になっていくことは明らかであり、.NETもそれに合わせて、並列処理をより楽に書けるように進化している。

 ここでは、.NET 4で採用された「タスク並列ライブラリ(TPL:Task Parallel Library)」と「PLINQ(Parallel LINQ)」について説明していこう。

タスク並列ライブラリ(TPL)

 タスク並列ライブラリは、System.Threading.Tasks名前空間に含まれているクラス群のことを指す。いくつかのクラスが含まれているが、大きくいうと並列処理を行う「Parallelクラス」と、非同期処理を行う「Taskクラス」の2つに分けられる。

Parallelクラス

 Parallelクラスの簡単なサンプルを紹介しよう。

Parallel.Invoke(
  Sub()
    For i As Integer = 0 To 5
      Console.WriteLine("A " & i)
      Thread.Sleep(1)
    Next
  End Sub,
  Sub()
    For i As Integer = 0 To 5
      Console.WriteLine("B " & i)
      Thread.Sleep(1)
    Next
  End Sub
)
Parallel呼び出しの例
2つの処理が並列に実行される。

 このコードでは、1つ目の処理で「A 」と実行回数を、2つ目の処理で「B 」と実行回数を出力するようにしてある。この2つの処理が並列で実行されるわけだ。なお、「Sleep」を入れて処理が多少ばらけるようにしてある。これを実行した結果は毎回変わるが、おおむね次のように並列に実行された結果になる。

A 0
A 1
B 0
A 2
B 1
A 3
B 2
A 4
B 3
A 5
B 4
B 5
Parallel呼び出しをした結果
「A」と「B」がばらばらに出力されていることが分かる。

 ParallelクラスにはInvokeメソッド以外にも、ForメソッドとForEachメソッドが用意されており、反復処理を並列に実行できるようになっている。

 Forメソッドを呼び出したコードを見ていこう。

Parallel.For(0, 10, Sub(x)
                      Console.WriteLine(x)
                    End Sub)
Parallel.Forメソッドを呼び出した例
「0」から「9」までの10回分のループが並列で呼び出される。

 このコードを実行した結果は以下のようになる。なお、この結果も当然のことながら毎回同じであるとは限らない。

0
2
3
4
6
7
8
9
1
5
Parallel.Forメソッドを実行した結果
「0」から「9」までが連続せずに実行されている。

 このように、Parallel.ForメソッドやParallel.ForEachメソッドを呼び出すことで、反復処理の並列化も簡単に行えることが確認いただけたと思う。

Taskクラス

 次にTaskクラスを使ったサンプルを解説しよう。

 次のコードでは、Taskクラスを使って、2つの非同期処理を並列に実行している。また、CancellationTokenSourceクラスを使用して、処理をキャンセルできるようになっている。

' タスクをキャンセルするためのキャンセルトークンを作成する
Dim cancelSource As New CancellationTokenSource

' 1つ目のタスクを起動する
Task.Factory.StartNew(Sub()
                          While True
                              Console.Write("ぶんぶん")
                              Thread.Sleep(100)
                              ' キャンセルされた場合、処理を中断する。
                              If cancelSource.IsCancellationRequested Then
                                  Exit While
                              End If
                          End While
                      End Sub,
                      cancelSource.Token)

' 2つ目のタスクを起動する
Task.Factory.StartNew(Sub()
                          While True
                              Console.Write("ぱっぱ")
                              Thread.Sleep(500)
                              ' キャンセルされた場合、処理を中断する。
                              If cancelSource.IsCancellationRequested Then
                                  Exit While
                              End If
                          End While
                      End Sub,
                      cancelSource.Token)
' キーの入力待ち
Console.ReadLine()
' それぞれのタスクをキャンセルする
cancelSource.Cancel()
Taskクラスを使用した非同期処理の例
「ぶんぶん」を出力する非同期タスクと「ぱっぱ」を出力する非同期タスクをそれぞれ実行している。

 このコードを実行した結果は次のようになる。

ぶんぶんぱっぱぶんぶんぶんぶんぶんぶんぶんぶんぶんぶんぱっぱぶんぶんぶんぶんぶん
ぶんぶんぶんぶんぶんぱっぱぶんぶんぶんぶんぶんぶんぶんぶんぱっぱぶんぶんぶんぶん
ぶんぶんぶんぶんぶんぶんぱっぱぶんぶんぶんぶんぶんぶんぶんぶんぶんぶんぱっぱぶん
ぶんぶんぶんぶんぶんぶんぶんぶんぶんぱっぱぶんぶんぶんぶんぶんぶんぶんぶんぶんぶ
んぱっぱぶんぶんぶんぶんぶんぶんぶんぶんぶんぶんぱっぱぶんぶんぶんぶんぶんぶんぶ
んぶんぶんぶんぱっぱぶんぶんぶんぶんぶんぶんぶんぶんぶんぶんぱっぱぶんぶんぶんぶ
んぶんぶんぶんぶんぶんぶんぱっぱぶんぶんぶんぶんぶんぶんぶんぶんぶんぶんぱっぱぶ
んぶんぶんぶんぶんぶんぶんぶんぶんぶんぱっぱぶんぶんぶんぶんぶんぶんぶんぶんぶん
ぶんぱっぱぶんぶんぶんぶんぶんぶんぶんぶんぶんぶんぱっぱぶんぶんぶんぶんぶんぶん
Taskクラスによる非同期実行の結果
「ぶんぶん」と「ぱっぱ」がそれぞれランダムに実行されていることが分かる。

 ご覧いただいたとおり、それぞれの処理が非同期に実行されている。また、CancellationTokenSourceクラスのトークン(=Tokenプロパティの値)をそれぞれのタスクに渡すことで、キャンセルが可能になっている。上記のコードでは、それぞれのタスクが実行された後、Console.ReadLine()メソッドで、コンソールが入力待ちの状態になる。入力待ち状態でもタスクが実行され続けていることが分かる。[Enter]キーを入力することで、次の行のCancellationTokenSourceクラスのCancelメソッドが呼ばれ、それぞれの非同期タスクが終了する。

PLINQ

 Taskクラスを使った並列/非同期処理について説明してきたが、次はLINQの並列実行であるPLINQ(Parallel LINQ)について説明しよう。LINQ自体に関する解説は割愛するが、VS 2008から追加された機能で、集合に対するクエリ処理を記述できる。LINQに関する詳細は、「VBプログラマーのためのLINQ超入門」を参照していただきたい。

 PLINQの呼び出しは簡単で、集合(となっている変数)に続けて「.AsParallel」(メソッド)を書くだけだ。

 以下のサンプル・コードを見てほしい。「1」から「100」までの配列を作り、「.AsParallel」を付けたクエリを実行している。そこで並列実行された結果をコンソールに出力している。

Sub Main()
  Dim numbers = Enumerable.Range(1, 100)
  Dim query = From number In numbers.AsParallel
              Where Judge(number)
              Select number

  For Each number In query
    Console.WriteLine(number)
  Next
  Console.ReadLine()
End Sub

Function Judge(ByVal number As Integer) As Boolean
  Thread.Sleep(2)
  Return number Mod 5 = 0
End Function
PLINQのサンプル・コード
AsParallelメソッドをデータソースに記述して、並列実行している。

 AsParallelメソッドを追加することで、numbers配列を並列に処理できるようになっている。この処理を実行結果は、次のようになる。

15
20
35
40
55
70
80
85
95
5
10
25
30
45
50
60
65
75
90
100
AsParallelメソッドを付けた並列処理の実行結果
並列実行されているため、処理順序がばらばらになっていることが分かる。

 いかがだろうか。AsParallelメソッドを呼び出すだけで、並列処理が実現できていることがご理解いただけただろう。

 ここまでの説明で非同期処理が簡単に記述できることに驚かれた方もいるのではないだろうか。冒頭でも説明したが、今後、非同期や並列処理はますます需要が高まっていく。本稿では解説を割愛するが、昨年、米国で開催された開発者向けカンファレンス「PDC10」で、将来リリースされる予定のVisual Studio Async(「Async」は「非同期」という意味)が発表され、CTPバージョンがダウンロードできるようになっている。

5. Visual Studio 2010 SP1の新機能

/vbruntimeコンパイラ・オプション

 VBは、.NETに対応するとき、VB6以前との互換性を維持するために、「Microsoft.VisualBasic.dllというアセンブリに互換ライブラリを追加する」という手段を選択した。VB6で実現していた機能のほとんどが.NETでも提供されているため、「VBコンパイラが.NETの標準ライブラリを呼び出すようなコードを出力する」という実装方法もあったはずだが、「互換性を維持する」という大きな宿命と、.NETだけでは実現できなかった、遅延バインディングやOn Errorを使ったエラー・ハンドリングなどのVB機能を再現するために、「専用アセンブリを用意する」という方法を選択した。

 .NETがフルセットで提供されているうちは、これでも問題がなかったが、Xbox360(ゲーム機)やWindows Phone 7(スマートフォン)など、.NETのサブセットが使われる環境では、VBアセンブリが付属しないケースが出てきた。そのため、Xbox360のゲーム開発環境であるXNAやWindows Phone 7は、C#専用という状況が発生してしまった。

 このような問題を打開するために追加されたのが、/vbruntimeコンパイラ・オプションである。このオプションを使用すると、Microsoft.VisualBasic.dllアセンブリを使用しないアセンブリを作成するようになる。

 なお、VS 2010 SP1のプロジェクト・オプションでは、このオプションを設定するUI(ユーザー・インターフェイス)は提供されていないが、使用するフレームワークによっては、このオプションが指定されているようである。

 VBアセンブリを使用しないようにするためには、「/vbruntime-」のように後ろにマイナスを付与する。「/vbruntime*」のようにアスタリスクを付与した場合、「vbCr」などの一部の定数とConversionsクラス、AscW関数など一部の機能がアセンブリに埋め込まれる。このオプションを使用することによって、VBでもC#でもほぼ同じアセンブリが生成されるため、.NETのフルセットが提供されていない環境向けにも、アプリを開発できるようになる。

 ただし、逆にいえば、昔から使われているVBの記述方法が使えなくなってしまう。使えなくなる主な機能は以下のとおりだ。

  • 昔から使われているVBの関数:
       Left()、Mid()、Right()、MsgBox()など
  • On Errorスタイルのエラー・ハンドリング:
       On Error Goto、On Error Resume Nextなど
  • Like演算子:
       Dim isMatch As Boolean = "abc" Like "ab?"
  • My機能:
       My.User.Nameなど
  • 遅延バインディング
  • Module

6. まとめ

 VB 2008までは、C#に置いていかれていた印象があったVBだが、VB 2010ではC#に追いついて、ともに成長していこうとしていることがご理解いただけただろうか。

 今後は、言語機能や開発環境に限らず、サンプルや記事などもC#と同様に提供されるようになると明言されている。これからVBはC#と同時に.NETとともに成長していくことになる。「一度、VBを見限った」という方がいたら、新たなレールに乗ったVBを見直して、もう一度学んでいってほしい。end of article

 

 INDEX
  特集:VB 10概説
  Visual Basic 2010の新機能
    1.「_」(アンダースコア)が不要になった=「暗黙の行継続」
    2.C#から引き継がれた新機能:自動実装プロパティ/コレクション初期化子/複数行のラムダ式
    3.VB 2010の新機能:ジェネリックの共変性と反変性/動的プログラミングのサポート
  4.VB 2010の新機能:並列処理、 Visual Studio 2010 SP1の新機能

更新履歴
【2011/05/18】

 「Taskクラスを使用した非同期処理の例」のコードにおて、cancelSourceの記述が不足していました。お詫びして訂正させていただきます。





Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)
- PR -

注目のテーマ

業務アプリInsider 記事ランキング

本日 月間
ソリューションFLASH