Buku "Persaingan dalam C #. Pemrograman asinkron, paralel, dan multithread. Int 2. ed. "

gambarHalo, habrozhiteli! Jika Anda takut pemrograman kompetitif dan multithread, buku ini ditulis untuk Anda. Stephen Cleary memiliki 85 resep untuk bekerja dengan .NET dan C # 8.0 untuk pemrosesan paralel dan pemrograman asinkron. Persaingan telah menjadi metode yang diterima untuk mengembangkan aplikasi yang sangat skalabel, tetapi pemrograman bersamaan tetap merupakan tugas yang menakutkan. Contoh rinci dan komentar pada kode akan membantu untuk memahami bagaimana alat modern meningkatkan tingkat abstraksi dan menyederhanakan pemrograman kompetitif. Anda akan belajar cara menggunakan async dan menunggu operasi asinkron, memperluas kemampuan kode melalui penggunaan utas sinkron, mengeksplorasi potensi pemrograman paralel dengan pustaka TPL Dataflow,buat jalur aliran data dengan pustaka TPL Dataflow, gunakan Sistem berbasis LINQ. Fungsionalitas reaktif, gunakan koleksi yang aman dan tidak dapat diubah, lakukan pengujian unit terhadap kode kompetitif, kendalikan kumpulan benang, kendalikan pembatalan kerjasama yang benar, analisis skrip untuk menggabungkan metode kompetitif , gunakan semua fitur pemrograman berorientasi objek yang kompatibel secara asinkron, kenali dan buat adapter untuk kode yang menggunakan gaya pemrograman asinkron yang lama.menganalisis skrip untuk menggabungkan metode kompetitif, menggunakan semua fitur pemrograman berorientasi objek yang kompatibel secara asinkron, mengenali dan membuat adaptor untuk kode yang menggunakan gaya lama pemrograman asinkron.menganalisis skrip untuk menggabungkan metode kompetitif, menggunakan semua fitur pemrograman berorientasi objek yang kompatibel secara asinkron, mengenali dan membuat adaptor untuk kode yang menggunakan gaya lama pemrograman asinkron.

Dasar-dasar Pemrograman Paralel


4.1. Pemrosesan data paralel


Tugas


Ada koleksi data. Anda harus melakukan operasi yang sama dengan setiap item data. Operasi ini dibatasi secara komputasi dan mungkin memerlukan waktu.

Keputusan


Tipe Paralel berisi metode ForEach, yang dirancang khusus untuk tugas ini. Contoh berikut mendapatkan kumpulan matriks dan memutar matriks ini:

void RotateMatrices(IEnumerable<Matrix> matrices, float degrees)
{
   Parallel.ForEach(matrices, matrix => matrix.Rotate(degrees));
}

Mungkin ada situasi di mana perlu untuk membatalkan siklus sebelum waktunya (misalnya, jika nilai yang tidak valid terdeteksi). Contoh berikut membalik setiap matriks, tetapi jika ditemukan matriks yang tidak valid, loop akan terputus:

void InvertMatrices(IEnumerable<Matrix> matrices)
{
   Parallel.ForEach(matrices, (matrix, state) =>
   {
      if (!matrix.IsInvertible)
        state.Stop();
      else
        matrix.Invert();
   });
}

Kode ini menggunakan ParallelLoopState.Stop untuk menghentikan loop dan mencegah panggilan lebih jauh ke loop body. Perlu diingat bahwa siklusnya paralel, oleh karena itu, panggilan lain ke badan siklus mungkin sudah dibuat, termasuk panggilan untuk elemen yang mengikuti siklus saat ini. Dalam contoh kode yang diberikan, jika matriks ketiga tidak dapat dibalik, maka siklusnya terputus dan matriks baru tidak akan diproses, tetapi mungkin ternyata matriks lain sudah diproses (misalnya, keempat dan kelima).

Situasi yang lebih umum terjadi ketika Anda harus membatalkan loop paralel. Ini tidak sama dengan menghentikan siklus; siklus berhenti dari dalam dan dibatalkan di luar. Misalnya, tombol batal dapat membatalkan CancurTokenSource, membatalkan loop paralel, seperti dalam contoh berikut:

void RotateMatrices(IEnumerable<Matrix> matrices, float degrees,
      CancellationToken token)
{
   Parallel.ForEach(matrices,
         new ParallelOptions { CancellationToken = token },
         matrix => matrix.Rotate(degrees));
}

Harus diingat bahwa setiap tugas paralel dapat dilakukan dalam utas yang berbeda, oleh karena itu, setiap keadaan bersama harus dilindungi. Contoh berikut membalik setiap matriks dan menghitung jumlah matriks yang tidak bisa dibalik:

// :     .
//      
//    .
int InvertMatrices(IEnumerable<Matrix> matrices)
{
  object mutex = new object();
  int nonInvertibleCount = 0;
  Parallel.ForEach(matrices, matrix =>
  {
     if (matrix.IsInvertible)
    {
       matrix.Invert();
    }
    else
    {
       lock (mutex)
      {
         ++nonInvertibleCount;
      }
    }
  });
  return nonInvertibleCount;
}

Penjelasan


Metode Parallel.ForEach menyediakan pemrosesan paralel untuk urutan nilai. Solusi LINQ Paralel (PLINQ) yang serupa menyediakan kemampuan yang hampir sama dalam sintaksis mirip LINQ. Salah satu perbedaan antara Parallel dan PLINQ adalah bahwa PLINQ mengasumsikan bahwa ia dapat menggunakan semua core pada komputer, sementara Parallel dapat secara dinamis merespon perubahan kondisi prosesor.

Paralel. FOREach mengimplementasikan loop foreach paralel. Jika Anda perlu menjalankan paralel untuk loop, kelas Parallel juga mendukung metode Parallel.For. Metode Parallel.For sangat berguna ketika bekerja dengan banyak array data yang menerima indeks tunggal.

informasi tambahan


Resep 4.2 membahas agregasi paralel dari serangkaian nilai, termasuk penjumlahan dan perhitungan rata-rata.

Resep 4.5 mencakup dasar-dasar PLINQ.

Bab 10 membahas tentang pembatalan.

4.2. Agregasi paralel


Tugas


Diperlukan untuk mengagregasi hasil pada akhir operasi paralel (contoh agregasi adalah penjumlahan nilai atau perhitungan rata-rata).

Keputusan


Untuk mendukung agregasi, kelas Paralel menggunakan konsep nilai lokal - variabel yang ada secara lokal dalam loop paralel. Ini berarti bahwa loop body hanya dapat mengakses nilai secara langsung, tanpa perlu sinkronisasi. Ketika loop siap untuk mengumpulkan semua hasil lokalnya, ia melakukan ini menggunakan delegasi localFinally. Perlu dicatat bahwa delegasi localFinally tidak perlu menyinkronkan akses ke variabel untuk menyimpan hasilnya. Contoh penjumlahan paralel:

// :     .
//      
//    .
int ParallelSum(IEnumerable<int> values)
{
  object mutex = new object();
  int result = 0;
  Parallel.ForEach(source: values,
        localInit: () => 0,
        body: (item, state, localValue) => localValue + item,
        localFinally: localValue =>
       {
          lock (mutex)
             result += localValue;
       });
  return result;
}

LINQ Paralel memberikan dukungan agregasi yang lebih komprehensif daripada kelas Paralel:

int ParallelSum(IEnumerable<int> values)
{
   return values.AsParallel().Sum();
}

Oke, itu trik yang murah karena PLINQ memiliki dukungan bawaan untuk banyak operator umum (seperti Sum). PLINQ juga menyediakan dukungan agregasi umum dengan operator Agregat:

int ParallelSum(IEnumerable<int> values)
{
  return values.AsParallel().Aggregate(
        seed: 0,
        func: (sum, item) => sum + item
  );
}

Penjelasan


Jika Anda sudah menggunakan kelas Paralel, Anda harus menggunakan dukungan agregasi. Dalam kasus lain, dukungan PLINQ biasanya lebih ekspresif, dan kode lebih pendek.

informasi tambahan


Resep 4.5 menguraikan dasar-dasar PLINQ.

4.3. Panggilan paralel


Tugas


Ada satu set metode yang harus dipanggil secara paralel. Metode-metode ini (kebanyakan) tidak tergantung satu sama lain.

Keputusan


Kelas Paralel berisi metode Invoke sederhana yang dirancang untuk skenario seperti itu. Dalam contoh berikut, array dibagi menjadi dua, dan dua bagian diproses secara terpisah:

void ProcessArray(double[] array)
{
   Parallel.Invoke(
         () => ProcessPartialArray(array, 0, array.Length / 2),
         () => ProcessPartialArray(array, array.Length / 2, array.Length)
   );
}

void ProcessPartialArray(double[] array, int begin, int end)
{
   // ,   ...
}

Anda juga bisa meneruskan array delegasi ke metode Parallel.Invoke jika jumlah panggilan tidak diketahui sebelum eksekusi:

void DoAction20Times(Action action)
{
   Action[] actions = Enumerable.Repeat(action, 20).ToArray();
   Parallel.Invoke(actions);
}

Parallel.Invoke mendukung pembatalan, seperti metode lain dari kelas Parallel:

void DoAction20Times(Action action, CancellationToken token)
{
   Action[] actions = Enumerable.Repeat(action, 20).ToArray();
   Parallel.Invoke(new ParallelOptions { CancellationToken = token },
        actions);
}

Penjelasan


Metode Parallel.Invoke adalah solusi hebat untuk panggilan paralel sederhana. Saya perhatikan bahwa itu tidak begitu cocok untuk situasi di mana perlu untuk mengaktifkan tindakan untuk setiap elemen data input (lebih baik menggunakan Parallel. ForEach untuk ini), atau jika setiap tindakan menghasilkan beberapa output (LINQ paralel harus digunakan sebagai gantinya).

informasi tambahan


Resep 4.1 membahas metode Parallel.ForEach, yang melakukan tindakan untuk setiap item data.

Resep 4.5 berkaitan dengan LINQ Paralel.

tentang Penulis


Stephen Cleary , pengembang berpengalaman, telah beralih dari ARM ke Azure. Dia berkontribusi pada open source Boost C ++ library dan merilis beberapa perpustakaan dan utilitas.

»Informasi lebih lanjut tentang buku itu dapat ditemukan di situs web penerbit
» Isi
» Kutipan

Untuk Khabrozhiteley Diskon 25% pada kupon - Cleary

Setelah pembayaran versi kertas buku, sebuah buku elektronik dikirim melalui email.

All Articles