0% menganggap dokumen ini bermanfaat (0 suara)
574 tayangan12 halaman

The Dining-Philosopher Problem

Masalah filsuf makan adalah contoh masalah sinkronisasi sumber daya yang digunakan untuk menggambarkan masalah kebuntuan. Ia menggambarkan lima filsuf yang bergantian makan dan berpikir dengan menggunakan dua garpu, tetapi hanya bisa makan jika mendapatkan kedua garpu. Berbagai solusi telah diusulkan untuk menghindari kebuntuan, termasuk hierarki sumber daya, konduktor, dan protokol permintaan

Diunggah oleh

KhairinaAfriani
Hak Cipta
© © All Rights Reserved
Kami menangani hak cipta konten dengan serius. Jika Anda merasa konten ini milik Anda, ajukan klaim di sini.
Format Tersedia
Unduh sebagai DOCX, PDF, TXT atau baca online di Scribd
0% menganggap dokumen ini bermanfaat (0 suara)
574 tayangan12 halaman

The Dining-Philosopher Problem

Masalah filsuf makan adalah contoh masalah sinkronisasi sumber daya yang digunakan untuk menggambarkan masalah kebuntuan. Ia menggambarkan lima filsuf yang bergantian makan dan berpikir dengan menggunakan dua garpu, tetapi hanya bisa makan jika mendapatkan kedua garpu. Berbagai solusi telah diusulkan untuk menghindari kebuntuan, termasuk hierarki sumber daya, konduktor, dan protokol permintaan

Diunggah oleh

KhairinaAfriani
Hak Cipta
© © All Rights Reserved
Kami menangani hak cipta konten dengan serius. Jika Anda merasa konten ini milik Anda, ajukan klaim di sini.
Format Tersedia
Unduh sebagai DOCX, PDF, TXT atau baca online di Scribd
Anda di halaman 1/ 12

The Dining-Philosopher Problem

Dalam ilmu komputer , para the dining-philosopher problem merupakan contoh masalah
yang sering digunakan dalam bersamaan desain algoritma untuk
menggambarkan sinkronisasi masalah dan teknik untuk menyelesaikan mereka.
Ini pada awalnya dirumuskan pada tahun 1965 oleh Edsger Dijkstra sebagai latihan ujian
mahasiswa, disajikan dalam hal komputerbersaing untuk akses ke peripheral tape
drive. Segera setelah itu, Tony Hoare memberikan masalah formulasi yang sekarang

Ilustrasi the dining-philosopher problem


Lima filsuf diam duduk di meja di sekitar semangkuk spaghetti . Garpu ditempatkan di antara
setiap pasangan filsuf yang berdekatan. (Sebuah perumusan masalah alternatif
menggunakannasi dan sumpit bukannya spaghetti dan garpu.)
Setiap filsuf harus bergantian berpikir dan makan. Namun, seorang filsuf hanya bisa makan
spaghetti ketika ia memiliki garpu kiri dan kanan. Setiap garpu dapat dipegang oleh satu
filsuf dan filsuf sehingga dapat menggunakan garpu hanya jika tidak sedang digunakan oleh
filsuf lain.Setelah ia selesai makan, ia harus meletakkan kedua garpu sehingga mereka
menjadi tersedia untuk orang lain. Seorang filsuf bisa ambil garpu di sebelah kanannya atau
satu di sebelah kiri sebagai mereka menjadi tersedia, tetapi tidak bisa mulai makan sebelum
mendapatkan keduanya.
Makan tidak dibatasi oleh jumlah spaghetti kiri: menganggap persediaan yang tidak terbatas.
Masalahnya adalah bagaimana merancang sebuah disiplin perilaku (a bersamaan algoritma )
sehingga setiap filsuf tidak akan kelaparan, yaitu selamanya dapat terus bergantian antara
makan dan berpikir, dengan asumsi bahwa filsuf apapun tidak dapat mengetahui ketika orang
lain mungkin ingin makan atau berpikir.
Masalahnya dirancang untuk menggambarkan masalah menghindari kebuntuan , keadaan
sistem di mana tidak ada kemajuan adalah mungkin. Untuk melihat bahwa merancang sebuah
solusi yang tepat untuk masalah ini tidak jelas, mempertimbangkan usulan berikut:
menginstruksikan setiap filsuf untuk berperilaku sebagai berikut:

berpikir sampai garpu kiri tersedia, ketika itu, mengambilnya;

berpikir sampai garpu yang tepat tersedia, ketika itu, mengambilnya;

ketika kedua garpu dipegang, makan untuk jumlah waktu yang tetap;

kemudian, menempatkan garpu kanan bawah;

kemudian, meletakkan garpu kiri bawah;

ulangi dari awal.

Upaya ini pada solusi gagal: memungkinkan sistem untuk mencapai keadaan kebuntuan, di
mana tidak ada kemajuan adalah mungkin.
Ini adalah keadaan di mana setiap filsuf telah mengambil garpu ke kiri, menunggu garpu ke
kanan untuk diletakkan. Dengan petunjuk yang diberikan, keadaan ini dapat dicapai, dan bila
sudah tercapai, para filsuf abadi akan menunggu satu sama lain untuk melepaskan garpu.
Kelaparan sumber daya mungkin juga terjadi secara independen dari kebuntuan jika seorang
filsuf tertentu tidak dapat memperoleh baik garpu karena masalah waktu. Misalnya mungkin
ada aturan bahwa para filsuf meletakkan garpu setelah menunggu sepuluh menit untuk garpu
lain untuk menjadi tersedia dan menunggu lebih lanjut sepuluh menit sebelum melakukan
upaya mereka berikutnya. Skema ini menghilangkan kemungkinan kebuntuan (sistem selalu
dapat maju ke negara yang berbeda) tetapi masih menderita masalah livelock .Jika semua
lima filsuf muncul di ruang makan pada waktu yang sama dan masing-masing mengambil
garpu kiri pada saat yang sama para filsuf akan menunggu sepuluh menit sampai mereka
semua meletakkan garpu mereka turun dan kemudian menunggu lebih lanjut sepuluh menit
sebelum mereka semua memilih mereka lagi.
Mutual exclusion adalah gagasan inti dari masalah, para filsuf makan membuat skenario
generik dan abstrak berguna untuk menjelaskan masalah jenis ini. Kegagalan filsuf ini
mungkin mengalami analog dengan kesulitan yang timbul dalam pemrograman komputer
nyata ketika beberapa program memerlukan akses eksklusif ke sumber daya bersama. Isu-isu
ini dipelajari dalam cabangPemrograman serentak . Masalah asli Dijkstra yang terkait dengan
perangkat eksternal seperti tape drive. Namun, kesulitan belajar di makan filsuf masalah
muncul jauh lebih sering ketika multiple access proses set data yang diperbarui. Sistem
seperti operasi kernel sistem, menggunakan ribuan kunci dan sinkronisasi yang
membutuhkan kepatuhan yang ketat untuk metode dan protokol jika masalah seperti
kebuntuan, kelaparan, atau korupsi data yang harus dihindari.
Sumber Daya solusi hirarki
Solusi ini untuk masalah ini adalah yang awalnya diusulkan oleh Dijkstra . Ini memberikan
sebuah perintah parsial terhadap sumber daya (garpu, dalam hal ini), dan membangun
konvensi bahwa semua sumber daya akan diminta dalam rangka, dan bahwa tidak ada dua
sumber yang tidak terkait dengan perintah yang pernah akan digunakan oleh satu unit kerja di
waktu yang sama. Di sini, sumber daya (garpu) akan diberi nomor 1 sampai 5 dan masingmasing unit kerja (filsuf) akan selalu mengambil rendah bernomor garpu pertama, dan
kemudian garpu lebih tinggi bernomor, dari antara dua garpu ia berencana untuk
menggunakan. Urutan bahwa setiap filsuf meletakkan garpu tidak masalah. Dalam hal ini,
jika empat dari lima filsuf sekaligus menjemput mereka garpu rendah bernomor, hanya garpu

bernomor tertinggi akan tetap di atas meja, sehingga filsuf kelima tidak akan mampu
mengambil garpu apapun. Selain itu, hanya satu filsuf akan memiliki akses ke tertinggi
bernomor garpu, jadi dia akan bisa makan menggunakan dua garpu.
Sementara solusi hirarki sumber daya menghindari deadlock, tidak selalu praktis, terutama
ketika daftar sumber daya yang dibutuhkan tidak sepenuhnya diketahui sebelumnya. Sebagai
contoh, jika unit kerja memegang sumber daya 3 dan 5 dan kemudian menentukan perlu
sumber daya 2, harus melepaskan 5, maka 3 sebelum mengambilnya 2, dan kemudian harus
kembali memperoleh 3 dan 5 dalam urutan itu. Program komputer yang mengakses sejumlah
besar catatan database tidak akan berjalan secara efisien jika mereka diminta untuk
melepaskan semua catatan yang lebih tinggi bernomor sebelum mengakses rekor baru,
membuat metode praktis untuk tujuan itu.
solusi Konduktor
Solusi lain relatif sederhana dicapai dengan memperkenalkan seorang pelayan di meja. Filsuf
harus meminta izin sebelum mengambil setiap garpu. Karena pelayan menyadari berapa
banyak garpu sedang digunakan, ia mampu menengahi dan mencegah kebuntuan.Ketika
empat dari garpu sedang digunakan, filsuf berikutnya untuk meminta seseorang untuk
menunggu izin pelayan, yang tidak diberikan sampai garpu telah dirilis. Logikanya ini dibuat
sederhana dengan menetapkan bahwa para filsuf selalu mencari untuk mengambil garpu
tangan kiri mereka sebelum tangan mereka garpu kanan (atau sebaliknya). Pelayan bertindak
sebagai semaphore , sebuah konsep yang diperkenalkan oleh Dijkstra pada tahun 1965.
Untuk menggambarkan bagaimana ini bekerja, pertimbangkan bahwa para filsuf diberi label
searah jarum jam dari A sampai E. Jika A dan C makan, empat garpu sedang digunakan. B
duduk antara A dan C sehingga memiliki garpu tidak tersedia, sedangkan D dan E memiliki
satu garpu terpakai antara mereka. Misalkan D ingin makan. Ketika ia ingin mengambil
garpu kelima, kebuntuan menjadi mungkin. Jika dia malah meminta pelayan dan disuruh
menunggu, kita bisa yakin bahwa waktu berikutnya dua garpu yang dirilis di sana pasti akan
ada setidaknya satu filsuf yang berhasil bisa meminta sepasang garpu. Oleh karena itu
kebuntuan tidak bisa terjadi.
Chandy / Misra solusi
Pada tahun 1984, K. Mani Chandy dan A. Aggarwal mengusulkan solusi yang berbeda untuk
masalah filsuf makan untuk memungkinkan agen sewenang-wenang (bernomor P 1 , ..., P n )
untuk bersaing untuk jumlah sewenang-wenang sumber daya, tidak seperti solusi
Dijkstra. Hal ini juga sepenuhnya didistribusikan dan tidak memerlukan otoritas sentral
setelah inisialisasi. Namun, itu melanggar persyaratan bahwa "para filsuf tidak berbicara satu
sama lain" (karena pesan permintaan).
1. Untuk setiap pasangan filsuf bersaing untuk sumber daya, membuat garpu dan
memberikannya kepada filsuf dengan ID yang lebih rendah. Setiap garpu dapat
menjadi kotor atau bersih. Awalnya, semua garpu kotor.
2. Ketika filsuf ingin menggunakan seperangkat sumber daya ( yaitu makan), ia harus
mendapatkan garpu dari tetangganya bersaing. Untuk semua garpu seperti dia tidak
punya, dia mengirimkan pesan permintaan.

3. Ketika seorang filsuf dengan garpu menerima pesan permintaan, dia terus garpu jika
itu bersih, tapi memberikan itu ketika itu kotor. Jika dia mengirimkan garpu di atas, ia
membersihkan garpu sebelum melakukannya.
4. Setelah seorang filsuf yang selesai makan, semua garpu nya menjadi kotor. Jika filsuf
lain sebelumnya telah meminta salah satu garpu, ia membersihkan garpu dan
mengirimkannya.
Solusi ini juga memungkinkan untuk gelar besar konkurensi, dan akan memecahkan masalah
sewenang-wenang besar.
Hal ini juga memecahkan masalah kelaparan. Label bersih / kotor bertindak sebagai cara
memberikan preferensi yang paling "kelaparan" proses, dan merugikan proses yang baru saja
"makan". Satu dapat membandingkan solusi mereka ke salah satu tempat filsuf tidak
diperbolehkan untuk makan dua kali berturut-turut tanpa membiarkan orang lain
menggunakan garpu di antaranya. Solusi mereka lebih fleksibel daripada itu, tetapi memiliki
unsur cenderung ke arah itu.
Dalam analisis mereka, mereka berasal sistem tingkat preferensi dari distribusi garpu dan
negara bersih / kotor mereka. Mereka menunjukkan bahwa sistem ini dapat menggambarkan
grafik asiklik, dan jika demikian, operasi dalam protokol mereka tidak dapat mengubah grafik
yang menjadi satu siklik. Hal ini menjamin bahwa deadlock tidak dapat terjadi. Namun, jika
sistem diinisialisasi ke keadaan sempurna simetris, seperti semua filsuf memegang garpu sisi
kiri mereka, maka grafik adalah siklik di awal, dan solusi mereka tidak dapat mencegah
kebuntuan. Memulai sistem sehingga filsuf dengan ID rendah memiliki garpu kotor
memastikan grafik awalnya asiklik.

Mengaktifkan C + + pengembang untuk menulis aplikasi yang sangat bersamaan adalah


fokus utama dari Visual Studio 2010. Rilis beta meliputi Runtime Concurrency, perpustakaan
paralel, dan alat-alat pengembangan yang bertujuan mengatasi beberapa masalah umum
mencegah pengembang dari membuka potensi kinerja yang melekat untuk hardware
multicore. Khususnya, hal ini termasuk memastikan bahwa pengembang dapat
mengidentifikasi dan memanfaatkan peluang untuk concurrency dalam kode mereka,
produktif mengelola negara bersama dan efek samping, dan tidak harus khawatir tentang
membangun infrastruktur concurrency overhead rendah yang terukur pada saat dijalankan
pada varietas perangkat keras.
Pada artikel ini, saya akan menunjukkan bagaimana menggunakan Agen Asynchronous baru
Perpustakaan dimasukkan sebagai bagian dari Visual C + + 2010 untuk mengelola kesulitan
yang dapat timbul dengan negara bersama. Untuk menunjukkan bagaimana ini bekerja, saya
akan berjalan melalui sebuah implementasi dari masalah konkurensi klasik: Djikstra yang
filsuf Dining. Anda akan melihat bagaimana pemrograman berbasis aktor membangun dari
agen dalam kombinasi dengan asynchronous pesan-lewat API dapat digunakan untuk
memberikan yang benar dan mudah dimengerti solusi untuk masalah ini yang tidak
bergantung langsung pada threading atau sinkronisasi primitif.
Para filsuf Dining

Bagi mereka yang tidak terbiasa dengan hal itu, Dining Philosophers masalah dimaksudkan
untuk menggambarkan kompleksitas pengelolaan negara bersama dalam lingkungan
multithreaded. Inilah masalahnya:
Pada meja bundar duduk lima filsuf yang bergantian antara berpikir dan makan dari mangkuk
besar nasi pada interval acak. Sayangnya bagi para filsuf, hanya ada lima sumpit di meja
(lihat Gambar 1 ), dan sebelum para filsuf bisa mulai makan, mereka harus mendapatkan
sepasang sumpit. Karena sumpit dibagi antara para filsuf, akses ke sumpit harus dilindungi,
tetapi jika perawatan tidak diambil, masalah konkurensi muncul. Terutama, kelaparan (pun
intended) melalui kebuntuan dapat terjadi: jika setiap filsuf mengambil sumpit secepat itu
tersedia dan menunggu tanpa batas waktu untuk yang lain, akhirnya mereka semua akan
berakhir memegang hanya satu sumpit dengan tidak ada kesempatan untuk mendapatkan
yang lain.

Gambar 1 Para filsuf dan Sumpit


The Dining Philosophers Masalah biasanya diwakili dalam kode oleh benang untuk setiap
filsuf dan beberapa bentuk negara bersama digunakan untuk mewakili masing-masing
sumpit. Solusi mudah untuk masalah ini sering melibatkan memperkenalkan entitas pelayan
untuk mengkoordinasikan akses ke sumpit, memperkenalkan kunci memesan heuristik, dan
secara manual bekerja dengan threading API, bagian kritis, Semaphore, atau monitor untuk
mengelola negara.
Kebanyakan pengembang akan setuju bahwa bangunan benar, penguncian trivial dianggap
menantang dan rapuh di terbaik. Akibatnya, sebagian besar implementasi dari Dining
Philosophers masalah cenderung memiliki rincian pelaksanaan bocor ke dalamnya. Misalnya,
para filsuf mungkin menyadari sinkronisasi primitif atau sumpit tetangga mereka. Hal ini
menjadi sangat bermasalah jika Anda ingin menggeneralisasi masalah untuk jumlah
sewenang-wenang filsuf atau memfokuskan kembali sebuah implementasi pada masalah
domain-seperti apa yang setiap filsuf tidak-daripada berfokus pada sinkronisasi dan primitif
threading.
Pendekatan Aktor Berbasis
Mari kita berjalan melalui desain yang dibangun di atas Asynchronous Agen
Perpustakaan. Makan filsuf benar-benar hanya memiliki dua bagian cukup sulit: menciptakan
thread untuk setiap filsuf untuk berjalan dalam dan mengkoordinasikan akses para filsuf
'untuk sumpit. The Asynchronous Agen Perpustakaan menyediakan model pemrograman
berbasis aktor dan pesan asynchronous lewat API, dan Anda akan perlu kedua dalam
pelaksanaannya.

Dalam Asynchronous Agen Perpustakaan, model agen kelas pola aktor dan menyediakan
metode run asynchronous. Saya menggunakan agen untuk setiap filsuf dan memanfaatkan
metode dijalankan untuk memenuhi kebutuhan setiap filsuf berjalan di thread sendiri.
Bagian kedua dari solusi yang tepat melibatkan mengelola akses bersamaan ke sumpit dan,
berbeda dengan solusi tradisional menggunakan Semaphore atau kunci, saya akan
menggunakan API lewat pesan dalam Asynchronous Agen Perpustakaan untuk memindahkan
sumpit antara tangan para filsuf 'dan tabel.
Karena setiap filsuf transisi antara berpikir dan makan, ia mengambil sumpit dan
mengembalikan mereka ke meja dengan mengirimkan pesan. Anda akan melihat bahwa
selain menyediakan beberapa abstraksi di atas benang dan negara bersama, ini juga akan
memungkinkan pelaksanaan hidup lebih fokus pada domain masalah daripada solusi lain
memungkinkan.
Solusi Dalam Lima Kelas
Desain dasar akan menggunakan lima jenis:

Sebuah kelas sumpit yang relatif Cukup jelas.

Sebuah kelas ChopstickProvider yang akan digunakan untuk memegang sumpit di


atas meja dan memberikan mereka kepada para filsuf.

Sebuah kelas Filsuf yang bertanggung jawab untuk berpikir dan makan dan
menyadari hanya dua ChopstickProviders.

Sebuah PhilospherState enum yang dapat berpikir atau makan.

Sebuah kelas Tabel yang berisi satu set filsuf dan satu set Sumpit. Tabel bertanggung
jawab untuk menata meja dengan menempatkan Chopstick tunggal dalam
ChopstickProvider antara setiap Filsuf.Gambar 2 menunjukkan hubungan antara kelas.

Gambar 2 Kelas Digunakan Dalam Dining Philosophers Solusi


Mari kita mulai dengan kelas sederhana: Chopstick. Tujuan dari Chopstick adalah untuk
hanya ada sebagai sumpit dan dapat mengidentifikasi dirinya. Akibatnya, implementasi
adalah kelas sederhana yang memiliki variabel anggota identifier, konstruktor yang
menginisialisasi pengenal, dan metode untuk kembali identifier.
Chopstick kelas {const std :: string m_Id, publik: Chopstick (std :: string && Id): m_Id (Id)
{}; std :: string const getId () {return m_Id;};};
Sangat penting untuk dicatat bahwa m_Id bidang identifier adalah variabel anggota
const. Saya tidak ingin sumpit untuk memiliki negara sendiri yang bisa berubah secara tidak

sengaja oleh thread lain. Juga perhatikan bahwa konstruktor menggunakan referensi r-nilai
dalam daftar parameter nya (&&). Ini adalah bahasa baru membangun dalam Visual Studio
2010 yang merupakan bagian dari rancangan C + +0 x standar. Sebuah referensi r-nilai di sini
memungkinkan compiler untuk menghindari salinan panggilan konstruktor dan memindahkan
Id ke m_Id dalam contoh Chopstick jika Id adalah variabel sementara atau r-nilai.
Pesan Blok Dan Pesan
ChopstickProvider juga mudah. Hal ini dapat dianggap sebagai buffer penyimpanan yang
tujuannya adalah untuk menerima Sumpit ketika mereka dikirim ke sana dan untuk
menyediakan sumpit ke Philosopher atas permintaan.
Berikut adalah tempat pertama di mana saya menggunakan API agen. Saya menggunakan apa
yang dikenal sebagai blok pesan untuk ChopstickProvider. Sebuah blok pesan didefinisikan
sebagai kelas yang mampu mengirim dan menerima pesan. Lebih khusus lagi, jika Anda
dapat mengirim pesan ke pesan blok, blok ini disebut sebagai target dan, jika Anda dapat
menerima pesan dari blok pesan, itu disebut sebagai sumber.
Dalam contoh ini, saya akan memerlukan ChopstickProvider untuk menjadi sumber dan
target karena para filsuf akan baik menerima sumpit dari ChopstickProviders ketika filsuf
lapar dan mengirim mereka kembali ke ChopstickProviders ketika filsuf siap untuk berpikir
lagi. Berikut ini adalah contoh menempatkan mereka untuk bekerja:
/ / Membuat sumpit sumpit sumpit ("sumpit satu"); / / membuat ChopstickProvider untuk
menyimpan sumpit ChopstickProvider chopstickBuffer; / / menempatkan sumpit dalam
sumpit pemegang Concurrency :: kirim (chopstickBuffer, & sumpit); / / meminta sumpit dari
sumpit penyangga sumpit * hasil = Concurrency :: terima (chopstickBuffer);
Anda dapat melihat bahwa, setelah menyatakan sumpit dan chopstickBuffer (contoh masih
terdefinisi ChopstickProvider), Concurrency :: kirim digunakan untuk menempatkan alamat
sumpit ke dalam ChopstickBuffer dan Concurrency :: menerima mengembalikan pointer ke
Chopstick dari chopstickBuffer.Concurrency :: kirim adalah metode template yang serentak
menempatkan pesan dalam blok pesan.Concurrency :: terima adalah metode template yang
mengembalikan pesan dari blok pesan. Concurrency :: menerima blok sampai pesan tersedia
di blok pesan.
The Asynchronous Agen Perpustakaan juga menyediakan Concurrency :: asend dan
Concurrency :: try_receive. Concurrency :: asend asynchronous memunculkan sebuah tugas
untuk mengirim pesan tanpa menunggu pengakuan penerimaan. Concurrency :: try_receive
adalah panggilan non-blocking yang akan memperoleh pesan jika tersedia.
Sekarang mari kita benar-benar mendefinisikan ChopstickProvider. Ingat saya mengatakan
itu mudah? Ini adalah typedef:
typedef Concurrency :: unbounded_buffer <Chopstick*> ChopstickProvider;
Para agen perpustakaan menyediakan blok pesan disebut unbounded_buffer yang memiliki
fungsi yang Anda butuhkan. Sebuah unbounded_buffer adalah kelas template yang baik
sumber dan target. Ini menyimpan sejumlah memori terbatas pesan dalam antrian
internal. Pesan akan dihapus dari antrian ketika mereka diminta. Anda tidak perlu kualitas tak
terbatas dalam pelaksanaan ini Dining Philosophers karena ada sejumlah sumpit yang akan
bergerak antara masing-masing pemegang sumpit. Fungsi tombol Anda tertarik dari
unbounded_buffer adalah semantik bergerak nya.
Agen Dan Bergabunglah Blok Pesan
Sekarang mari kita lihat menerapkan kelas Filsuf. Ini adalah bagian menarik dari Dining
Philosophers masalah, di mana Anda memastikan bahwa setiap filsuf berjalan pada thread

sendiri dan bahwa akses ke sumber daya bersama dikoordinasikan dengan tepat. Kelas
Philosopher akan menggunakan dua konstruksi tambahan dari Asynchronous Agen
Perpustakaan, kelas agen abstrak dan bergabung membangun. Pertama saya akan
menjelaskan agen dan bagaimana itu digunakan.
Para agen kelas merupakan kelas abstrak yang model apa yang kadang-kadang disebut
sebagai pola aktor atau pemrograman berbasis aktor. Agen dimaksudkan untuk digunakan
untuk memfasilitasi menghapus dependensi dan negara bersama antara komponen aktif dari
aplikasi dengan memiliki agen merangkum negara dan berkomunikasi satu sama lain hanya
melalui pesan melewati set kecil antarmuka publik. Ini terdengar lebih rumit dari itu, tapi
mudah-mudahan Anda dapat melihat kemiripan dengan kelas Philosopher.
Seperti yang saya sebutkan sebelumnya, kelas Filsuf harus berjalan pada thread
sendiri. Menerjemahkan ini ke terminologi agen ini berarti perlu menjadi tugas yang aktif,
sehingga Filsuf akan berasal dari publik Concurrency :: agen. Kelas Filsuf juga perlu
menyadari dua ChopstickProviders (satu untuk masing-masing tangan) dan kemudian
menggunakannya untuk transisi berhasil antara berpikir dan makan. Kelas Philosopher belum
lengkap, tetapi Anda dapat melihat bagaimana hal ini mulai diterjemahkan ke dalam sinopsis
kelas:
enum PhilosopherState {Berpikir, Makan}; typedef Concurrency :: unbounded_buffer
<Chopstick*> ChopstickProvider, Filsuf kelas: Concurrency public :: agen
{ChopstickProvider * m_LeftChopstickProvider, ChopstickProvider *
m_RightChopstickProvider; publik: const std :: string m_Name; const int m_Bites; Filsuf
(const std :: string && nama, int gigitan = 500): m_Name (nama), m_Bites (gigitan) {}; ... }
Filsuf memiliki pointer ke dua ChopstickProviders, nama, jumlah gigitan atau ternyata
Philosopher akan mengambil, dan konstruktor untuk inisialisasi.
Apa yang hilang, meskipun, adalah cara untuk menginisialisasi ChopstickProviders di
agen. Anda tidak ingin menginisialisasi mereka pada saat konstruksi karena filsuf yang
independen dari Sumpit dan ChopstickProviders mereka. Anda bisa membuat metode umum
tambahan seperti AssignChopsticks (ChopstickProvider * kiri, ChopstickProvider * kanan),
tapi kemudian Anda harus mengambil beberapa perawatan atau kunci-untuk memastikan
bahwa metode ini thread aman.
Sebaliknya, aku akan membuat antarmuka publik sehingga ChopstickProviders dapat
diberikan asynchronous. Saya melakukan ini dengan menambahkan dua variabel anggota
unbounded_buffer publik yang lebih dan menggunakan ini untuk mengirim dua filsuf
ChopstickProviders:
Concurrency :: unbounded_buffer <ChopstickProvider*> LeftChopstickProviderBuffer,
Concurrency :: unbounded_buffer <ChopstickProvider*> RightChopstickProviderBuffer;
Sekarang aku siap untuk mulai menerapkan metode Filsuf yang akan berjalan di thread
sendiri, menunggu ChopstickProviders harus diinisialisasi, dan kemudian mulai bergantian
antara berpikir dan makan. Saya menerapkan metode menjalankan virtual dari kelas dasar
agen. agent :: run akan dimulai pada tugas sendiri ketika panggilan ke agen :: mulai dibuat.
void run () {
Hal pertama yang harus saya lakukan dalam menjalankan adalah menginisialisasi
ChopstickProviders dengan menunggu pesan yang akan dikirim pada metode umum dengan
menerima:
/ / Menginisialisasi ChopstickProviders m_LeftChopstickProvider = Concurrency :: terima
(LeftChopstickProviderBuffer); m_RightChopstickProvider = Concurrency :: terima
(RightChopstickProviderBuffer);
Sekarang saya perlu transisi antara berpikir dan makan. Untuk melakukan hal ini saya akan
menggunakan dua metode tambahan, PickupChopsticks dan PutDownChopsticks:

for (int i = 0; i <m_Bites, + + i) {Think (); std :: vector <Chopstick*> sumpit


(PickupChopsticks ()); Makan (); PutDownChopsticks (sumpit);}
Satu-satunya hal yang tersisa untuk menerapkan dalam metode run adalah untuk
membersihkan dengan mengembalikan ChopstickProviders sehingga mereka dapat
digunakan kembali jika diperlukan, dan untuk menetapkan status agen untuk dilakukan.
Concurrency :: kirim (LeftChopstickProviderBuffer, m_LeftChopstickProvider);
Concurrency :: kirim (RightChopstickProviderBuffer, m_RightChopstickProvider), ini->
dilakukan (Concurrency :: agent_done);}
Pindah, metode tambahan Makan dan Pikirkan yang mudah. Dalam masalah asli mereka
digambarkan sebagai loop berputar acak:
swasta: void Makan () {RandomSpin ();}; kekosongan Pikirkan () {RandomSpin ();};};
Pelaksana PickupChopsticks secara signifikan lebih menarik karena berhasil mendapatkan
dan melepaskan sumpit tanpa memperkenalkan kebuntuan atau kondisi ras. Untuk
melaksanakan ini saya akan menggunakan blok pesan lain dari Agen Perpustakaan
Asynchronous disebut bergabung. Concurrency :: bergabung adalah blok pesan yang
menunggu pesan dari berbagai sumber, berpotensi dari berbagai jenis. Setelah semua pesan
telah diterima, menghasilkan pesan konglomerat. Sebuah join dapat mendukung sumber dari
jenis yang sama atau ganda dan akan memperoleh pesan secara serakah atau non-serakah. Ini
berarti bahwa ada empat varian bergabung dalam agen perpustakaan. Kelas Philosopher akan
menggunakan satu tipe bergabung non-serakah. Gambar 3 menunjukkan contoh sederhana
dari bergabung dalam kode.
Gambar 3 Sebuah Gabung Sederhana
/ / Membuat dua sumpit Chopstick chopstick1 ("sumpit satu"); Chopstick chopstick2
("sumpit dua"); / / membuat ChopstickProviders untuk menyimpan sumpit ChopstickProvider
chopstickBuffer1, ChopstickProvider chopstickBuffer2, / / menempatkan sumpit di setiap
sumpit pemegang Concurrency :: kirim (chopstickBuffer1, & chopstick1); Concurrency ::
kirim (chopstickBuffer2, & chopstick2); / / mendeklarasikan satu-jenis non serakah
bergabung untuk mendapatkan mereka. / / Parameter konstruktor adalah jumlah input
Concurrency :: bergabung <Chopstick*,non_greedy> j (2); / / menghubungkan penyedia
sumpit untuk bergabung sehingga pesan / / dikirim akan merambat ke depan
chopstickBuffer1.link_target (& j); chopstickBuffer2.link_target (& j); / / tipe tunggal
bergabung pesan blok menghasilkan vektor Sumpit std :: vector <Chopstick*> hasil =
Concurrency :: terima (j);
Sekarang aku siap untuk melaksanakan PickupChopsticks. Hal pertama yang harus
diperhatikan adalah bahwa Anda kembali satu set sumpit dengan kembali vektor. Untuk
mendapatkan sepasang sumpit dari penyedia sumpit berarti menggunakan join nonserakah. Non-serakah varian bergabung menunggu tawaran pesan dari semua sumber terkait,
dan kemudian, setelah memiliki tawaran dari masing-masing sumber, menegaskan bahwa hal
itu benar-benar dapat mengambil kepemilikan pesan. Inilah yang mencegah kebuntuan dalam
contoh ini. Jika saya telah menggunakan serakah sebagai parameter template untuk
bergabung, pesan akan diambil secepat menawarkan dibuat, dan cepat atau lambat setiap
Philosopher akan memiliki sumpit tunggal dan mereka semua akan menemui jalan buntu.
Berikut kode untuk PickupChopsticks. Saya membuat join, link ke penyedia sumpit, dan
menunggu sumpit tiba:
std :: vector <Chopstick*> PickupChopsticks () {/ / membuat Concurrency join :: bergabung
<Chopstick*,Concurrency::non_greedy> j (2); m_LeftChopstickProvider-> Nama_Pranala
(& j); m_RightChopstickProvider-> Nama_Pranala (& j) ; / / pickup sumpit kembali
Concurrency :: terima (j);}

Pelaksana PutDownChopsticks sangat mudah juga. Semua harus saya lakukan adalah
mengembalikan sumpit yang saya diperoleh dari vektor menggunakan metode asend
asynchronous:
PutDownChopsticks void (std :: vector <Chopstick*> & v) {Concurrency :: asend
(m_LeftChopstickProvider, v [0]); Concurrency :: asend (m_RightChopstickProvider, v
[1]);};
Pengujian Philosopher Dan Menampilkan Negara
Kelas Philosopher dapat digunakan sebagai, tetapi Anda perlu memahami bagaimana untuk
memulai kelas Filsuf dan memiliki cara melaporkan statusnya-apakah itu makan atau
berpikir.
Untuk memulai filsuf, hubungi agen :: metode start, yang memunculkan sebuah tugas yang
memanggil method run virtual. Untuk melaporkan status, saya memperkenalkan dua blok lagi
pesan dari agen perpustakaan: Concurrency :: overwrite_buffer dan Concurrency :: panggilan.
Concurrency :: overwite_buffer <T> adalah blok pesan yang menyimpan nilai tunggal yang
dapat ditimpa, dan menghasilkan salinan nilai ketika diminta. Seperti dengan blok-blok pesan
lainnya, overwrite_buffer bisa dihubungkan dengan target tambahan, dan propagasi pesan
yang terjadi dalam rangka. Saya akan menambahkan variabel anggota public untuk kelas
Philosopher yang merupakan <PhilosopherState> overwrite_buffer dan memperbaruinya
dengan mengirimkan pesan itu sebagai transisi dari Filsuf Berpikir untuk Makan. Secara
khusus, ini akan melibatkan menambahkan variabel anggota untuk kelas Philosopher:
Concurrency :: overwrite_buffer <PhilosopherState> CurrentState;
Ini juga berarti memodifikasi Makan dan Pikirkan untuk memperbarui statusnya:
membatalkan Makan () {send (& CurrentState, PhilosopherState :: Makan); RandomSpin
();}; kekosongan Pikirkan () {send (& CurrentState, PhilosopherState :: Berpikir);
RandomSpin ();};
Concurrency :: panggilan <T> adalah blok pesan yang dibangun dengan functor dan
memunculkan sebuah tugas untuk mengeksekusi functor ketika menerima pesan. Anda dapat
menggunakan panggilan dalam kombinasi dengan overwrite_buffer baru didefinisikan pada
Filsuf untuk melaporkan keadaan, seperti yang ditunjukkan pada Gambar 4 .
Gambar 4 Pelaporan Negara Filsuf
/ / Membuat sebuah instance dari Filsuf dan memilikinya mengambil 5 gigitan Filsuf
Descartes ("Descartres", 5); / / menciptakan sepasang sumpit Chopstick chopstick1
("chopstick1"); Chopstick chopstick2 ("chopstick2"); / / buat sepasang ChopstickProviders
ChopstickProvider chopstickBuffer1, ChopstickProvider chopstickBuffer2, / /
mengasosiasikan penyedia sumpit kami dengan Descartes Concurrency :: kirim (&
Descartes.LeftChopstickProvider, chopstickBuffer1); Concurrency :: kirim (&
Descartes.RightChopstickProvider, chopstickBuffer2); / / memulai Filsuf asynchronous
agent: : memulai (& Descartes); / / deklarasikan sebuah panggilan yang akan digunakan
untuk menampilkan filsuf negara / / perhatikan ini menggunakan C + +0 x lambda
Concurrency :: panggilan <PhilosopherState> display ([] (PhilosopherState negara) {if
( Nama_Pranala / / link panggilan untuk buffer status di Descartes.CurrentState Filsuf kami,
negara == Makan) std :: cout << "makan \ n"; lain std :: cout << "berpikir \ n";}). (& display);
/ / start Filsuf kami makan / berpikir dengan mengirimkan sumpit asend (& chopstickBuffer1,
& chopstick1); asend (& chopstickBuffer2, & chopstick2);
Dan itu untuk kelas Filsuf.

Pelaksana Kelas Table


Anda sudah melihat semua yang diperlukan untuk membuat kelas Table. Seperti disebutkan
sebelumnya, akan berisi satu set filsuf, satu set Sumpit, dan satu set
ChopstickProviders. Tabel akan bertanggung jawab untuk tempat duduk para filsuf di meja,
menempatkan ChopstickProvider antara masing-masing dari mereka, dan menempatkan
Chopstick di setiap ChopstickProviders. Untuk fleksibilitas Saya telah memilih untuk
membuat sebuah template kelas, dan berisi referensi ke daftar filsuf, vektor Chopsticks, dan
vektor Pemegang Chopstick.
template class PhilosopherList> kelas Table {PhilosopherList & m_Philosophers, std :: vector
<ChopstickProvider*> m_ChopstickProviders, std :: vector <Chopstick*> m_Chopsticks; ...
Satu-satunya metode umum di kelas Tabel adalah konstruktor, yang menginisialisasi vektor
dan penerima ChopstickProviders kepada setiap Philosopher (lihat Gambar 5 ).
Gambar 5 Tabel Konstruktor
Tabel (PhilosopherList & filsuf): m_Philosophers (filsuf) {/ / mengisi sumpit dan vektor
pemegang sumpit untuk (size_t i = 0; i <m_Philosophers.size (); + + i)
{m_ChopstickProviders.push_back (baru ChopstickProvider ()) ; m_Chopsticks.push_back
(Chopstick baru ("sumpit")); / / menempatkan sumpit di penyedia sumpit kirim
(m_ChopstickProviders [i], m_Chopsticks [i]);} / / menetapkan filsuf ke tempat-tempat
mereka (leftIndex size_t = 0; leftIndex <m_Philosophers.size (); + + leftIndex) {/ /
menghitung rightIndex int rightIndex = (leftIndex +1)% m_Philosophers.size (); / / mengirim
penyedia sumpit kiri & kanan ke Concurrency Philosopher yang tepat: : asend (. &
m_Philosophers [leftIndex] LeftChopstickProviderBuffer, m_ChopstickProviders
[leftIndex]); Concurrency :: asend (. & m_Philosophers [leftIndex]
RightChopstickProviderBuffer, m_ChopstickProviders [rightIndex]);}} ~ Tabel ()
{m_ChopstickProviders.clear (); m_Chopsticks . jelas ();}
Time For Lunch
Akhirnya, untuk menerapkan utama, saya perlu menyatakan daftar filsuf, membuat Tabel,
mulai filsuf, memasang sebuah mekanisme pelaporan, dan menunggu mereka untuk makan
(lihat Gambar 6 ).
Gambar 6 Mealtime
int main () {/ / membuat satu set filsuf menggunakan array std tr1 :: TR1 :: array
<Philosopher,5> filsuf = {"Socrates", "Descartes", "Nietzsche", "Sartre", "Amdahl" }; Tabel
<std :: TR1 :: array <Philosopher,5>> Table (filsuf); / / membuat daftar blok panggilan untuk
menampilkan std :: vector <Concurrency :: panggilan <PhilosopherState> *> display; / /
memulai filsuf dan membuat tampilan barang std :: for_each (philosophers.begin (),
philosophers.end (), [& display] (Filsuf & skr) {/ / membuat blok panggilan baru untuk
menampilkan Concurrency Status :: <PhilosopherState> panggilan * consoleDisplayBlock =
Concurrency baru :: panggilan <PhilosopherState> ([&] (PhilosopherState in) {/ / cout tidak
threadsafe antara setiap produksi jika (dalam == Makan) std :: cout << cur.m_Name <<
"adalah makan \ n "; std lain :: cout << cur.m_Name <<" adalah pemikiran \ n ";}); / / link up
display dan menyimpannya di cur.CurrentState.link_target vektor (consoleDisplayBlock);
display. push_back (consoleDisplayBlock); / / dan memulai cur.start agent ();}); / / menunggu
mereka untuk menyelesaikan std :: for_each (philosophers.begin (), philosophers.end (), []
(Filsuf & skr) {cur.wait (& skr);}); displays.clear (); return 1;};
Mudah-mudahan, itu jelas bagaimana kelas dan asynchronous pesan lewat agen bahwa
Asynchronous Agen Perpustakaan menyediakan dapat membantu menghindari kesulitan
mengkoordinasikan akses ke negara bersama. Saya sudah berhasil melaksanakan Dining

Philosophers masalah tanpa menggunakan kunci eksplisit tunggal dan tanpa harus memanggil
API threading langsung. Saya juga berhasil menjaga pelaksanaan dalam hal waktu domainspesifik, saya telah menghabiskan bekerja dengan Chopsticks, filsuf, dan Tabel sebagai lawan
array bilangan bulat dan Semaphore.
Apa yang saya belum selesai ditambahkan setiap skalabilitas yang melekat pada para
filsuf. Aplikasi ini seperti yang tertulis tidak akan berjalan secara signifikan lebih cepat pada
setiap lebih dari empat core.Tapi ini bisa diperbaiki dengan mengganti RandomSpin dengan
sesuatu yang lebih bermakna, seperti algoritma diimplementasikan dengan paralelisme tugas
berbasis menggunakan Pola Perpustakaan Paralel (PPL). Saya mendorong Anda untuk
membaca tentang ini di blog konkurensi asli , di mana saya punya kode sumber untuk artikel
ini, tapi di mana saya juga menambahkan algoritma berpikir paralel halus beberapa
diimplementasikan untuk menunjukkan composability antara PPL dan agen
perpustakaan. Saya juga telah menggunakan fitur manajemen sumber daya Concurrency
Runtime untuk mengelola masalah mutu pelayanan yang dapat timbul dengan kelimpahan
pekerjaan paralel. Ketika pengolahan sumber daya semakin langka, saya dapat memastikan
bahwa para filsuf masih bisa mendapatkan cukup untuk makan.

Anda mungkin juga menyukai