Kunci, mutex, semaphore ... apa bedanya?


Jawaban:


534

Kunci hanya memungkinkan satu utas untuk memasuki bagian yang dikunci dan kunci tidak dibagi dengan proses lain.

Mutex sama dengan kunci tetapi bisa lebar sistem (dibagi oleh beberapa proses).

Sebuah semaphore melakukan hal yang sama seperti mutex tetapi memungkinkan x jumlah benang untuk masuk, ini dapat digunakan misalnya untuk membatasi jumlah cpu, io atau ram tugas-tugas intensif berjalan pada waktu yang sama.

Untuk posting yang lebih detail tentang perbedaan antara mutex dan semaphore baca di sini .

Anda juga memiliki kunci baca / tulis yang memungkinkan pembaca tanpa batas atau 1 penulis pada waktu tertentu.


2
@mertinan saya tidak bisa mengatakan saya pernah mendengar tentang itu, tapi inilah yang dikatakan wikipedia "Latch (database), (yang relatif singkat) mengunci sistem data-struktur seperti indeks"
Peter

2
Monitor memungkinkan untuk menunggu kondisi tertentu (misalnya ketika kunci dilepaskan), "monitor".
Dzmitry Lazerka

25
Semaphore tidak sama dengan mutex. Mereka digunakan sangat berbeda dan juga memiliki sifat yang berbeda (yaitu mengenai kepemilikan). Lihat misalnya barrgroup.com/Embedded-Systems/How-To/RTOS-Mutex-Semaphore untuk detail
nanoquack

3
@nanoquack merasa bebas untuk mengedit jawaban saya jika Anda merasa itu menyesatkan atau salah.
Peter

3
Untuk perbedaan yang lebih jelas antara mutex dan semaphore, dalam tautan nanoquack, Paragraf kuncinya adalah " Penggunaan semaphore yang benar adalah untuk pensinyalan dari satu tugas ke tugas lainnya. Suatu mutex dimaksudkan untuk diambil dan dirilis, selalu dalam urutan itu, oleh masing-masing tugas yang menggunakan sumber daya bersama yang dilindunginya. Sebaliknya, tugas yang menggunakan semafor memberi sinyal atau menunggu — bukan keduanya. "
ToolmakerSteve

117

Ada banyak kesalahpahaman tentang kata-kata ini.

Ini dari pos sebelumnya ( https://stackoverflow.com/a/24582076/3163691 ) yang sangat cocok di sini:

1) Bagian Kritis = Objek pengguna yang digunakan untuk memungkinkan pelaksanaan hanya satu utas aktif dari banyak lainnya dalam satu proses . Utas yang tidak dipilih lainnya (@ mendapatkan objek ini) dimatikan .

[Tidak ada kemampuan proses, objek yang sangat primitif].

2) Mutex Semaphore (alias Mutex) = Objek kernel yang digunakan untuk memungkinkan eksekusi hanya satu utas aktif dari banyak lainnya, di antara proses yang berbeda . Utas yang tidak dipilih lainnya (@ mendapatkan objek ini) dimatikan . Objek ini mendukung kepemilikan utas, pemberitahuan penghentian utas, rekursi (beberapa panggilan 'dapatkan' dari utas yang sama) dan 'penghindaran inversi prioritas'.

[Kemampuan interprocess, sangat aman digunakan, semacam objek sinkronisasi 'tingkat tinggi'].

3) Menghitung Semaphore (alias Semaphore) = Objek kernel yang digunakan untuk memungkinkan eksekusi sekelompok utas aktif dari banyak lainnya. Utas yang tidak dipilih lainnya (@ mendapatkan objek ini) dimatikan .

[Namun kemampuan interproses tidak terlalu aman untuk digunakan karena tidak memiliki atribut 'mutex' berikut: pemberitahuan penghentian utas, rekursi ?, 'penghindaran inversi prioritas'?, Dll].

4) Dan sekarang, berbicara tentang 'spinlocks', pertama beberapa definisi:

Wilayah Kritis = Wilayah memori yang digunakan bersama oleh 2 proses atau lebih.

Kunci = Variabel yang nilainya memungkinkan atau menolak pintu masuk ke 'wilayah kritis'. (Ini bisa diimplementasikan sebagai 'bendera boolean' yang sederhana).

Busy waiting = Pengujian variabel secara berkesinambungan hingga beberapa nilai muncul.

Akhirnya:

Spin-lock (alias Spinlock) = Kunci yang menggunakan menunggu sibuk . (Pengambilan kunci dilakukan oleh xchg atau operasi atom serupa ).

[Tanpa thread thread, kebanyakan digunakan hanya pada level kernel. Tidak efisien untuk kode level Pengguna].

Sebagai komentar terakhir, saya tidak yakin tetapi saya dapat bertaruh Anda banyak uang bahwa 3 objek sinkronisasi pertama di atas (# 1, # 2 dan # 3) menggunakan binatang sederhana ini (# 4) sebagai bagian dari implementasinya.

Semoga harimu menyenangkan!.

Referensi:

-Real-Time Konsep untuk Sistem Tertanam oleh Qing Li dengan Caroline Yao (CMP Books).

-Sistem Operasi Modern (3) oleh Andrew Tanenbaum (Pearson Education International).

-Pemrograman Aplikasi untuk Microsoft Windows (4) oleh Jeffrey Richter (Microsoft Programming Series).

Anda juga dapat melihat: https://stackoverflow.com/a/24586803/3163691


1
Sebenarnya bagian kritis bukanlah objek kernel, sehingga lebih ringan dan tidak dapat disinkronkan di seluruh proses.
Vladislavs Burakovs

2
@ Vladislavs Burakovs: Anda benar! Maafkan redaksi saya. Saya akan memperbaikinya demi koherensi.
fante

Untuk perbedaan yang lebih jelas antara mutex dan semaphore, seperti yang disebutkan nanoquack di tempat lain, lihat barrgroup.com/Embedded-Systems/How-To/RTOS-Mutex-Semaphore - Paragraf kuncinya adalah " Penggunaan semafor yang benar untuk memberi sinyal dari satu tugas ke yang lain. Sebuah mutex dimaksudkan untuk diambil dan dirilis, selalu dalam urutan itu, oleh setiap tugas yang menggunakan sumber daya bersama yang dilindunginya. Sebaliknya, tugas-tugas yang menggunakan semafor memberi sinyal atau menunggu — bukan keduanya. "
ToolmakerSteve

Menebak kembali mekanisme kunci lain yang dibangun di atas spinlock [tidak efisien]: tidak mungkin; AFAIK hanya perlu beberapa operasi atom ditambah antrian tidur. Bahkan di mana spinlock yang diperlukan dalam kernel, solusi modern yang meminimalkan dampaknya seperti yang dijelaskan di Wikipedia - spinlock - Alternatif - " .. menggunakan pendekatan hybrid yang disebut 'adaptive mutex' Idenya adalah untuk menggunakan spinlock ketika mencoba untuk mengakses sumber daya dikunci oleh. utas yang saat ini berjalan, tetapi untuk tidur jika utas tidak saat ini berjalan (yang terakhir selalu terjadi pada sistem prosesor tunggal.) "
ToolmakerSteve

@ToolmakerSteve, saya berani Anda memberikan 'solusi' tanpa 'spinlock' untuk masalah 'tabrakan' saat mencoba 'memasukkan' utas ID ke 'antrian tidur'. Bagaimanapun, teks Wikipedia menyimpulkan bahwa spinlock digunakan pada implementasi !!!.
fante

27

Sebagian besar masalah dapat diselesaikan dengan menggunakan (i) hanya mengunci, (ii) hanya semafor, ..., atau (iii) kombinasi keduanya! Seperti yang mungkin Anda temukan, keduanya sangat mirip: keduanya mencegah kondisi balapan , keduanya memiliki acquire()/ release()operasi, keduanya menyebabkan nol atau lebih utas menjadi tersumbat / dicurigai ... Sungguh, perbedaan yang krusial hanya terletak pada bagaimana mereka mengunci dan membuka kunci .

  • Sebuah kunci (atau mutex ) memiliki dua negara (0 atau 1). Itu bisa dibuka atau dikunci . Mereka sering digunakan untuk memastikan hanya satu utas memasuki bagian kritis pada suatu waktu.
  • Sebuah semaphore memiliki banyak negara (0, 1, 2, ...). Itu dapat dikunci (status 0) atau tidak terkunci (status 1, 2, 3, ...). Satu atau lebih semaphore sering digunakan bersama untuk memastikan bahwa hanya satu utas memasuki bagian kritis tepat ketika jumlah unit dari beberapa sumber daya telah / belum mencapai nilai tertentu (baik dengan menghitung mundur ke nilai itu atau menghitung hingga nilai itu ).

Untuk kedua kunci / semafor, mencoba menelepon acquire()saat primitif dalam keadaan 0 menyebabkan utas penangguhan ditangguhkan. Untuk kunci - upaya untuk mendapatkan kunci dalam keadaan 1 berhasil. Untuk semafor - upaya untuk mendapatkan kunci di status {1, 2, 3, ...} berhasil.

Untuk kunci dalam keadaan 0, jika utas yang sama yang sebelumnya disebut acquire(), sekarang panggilan rilis, maka rilis berhasil. Jika utas yang berbeda mencoba ini - ini tergantung pada implementasi / pustaka tentang apa yang terjadi (biasanya upaya diabaikan atau kesalahan dilemparkan). Untuk semafor dalam status 0, utas apa pun dapat memanggil rilis dan itu akan berhasil (terlepas dari utas mana yang sebelumnya digunakan untuk meletakkan semafor dalam status 0).

Dari diskusi sebelumnya, kita dapat melihat bahwa kunci memiliki gagasan tentang pemilik (utas satu-satunya yang dapat memanggil pelepasan adalah pemilik), sedangkan semafor tidak memiliki pemilik (utas apa pun dapat menyebut pelepasan pada semafor).


Apa yang menyebabkan banyak kebingungan adalah bahwa, dalam praktiknya ada banyak variasi definisi tingkat tinggi ini.

Variasi penting untuk dipertimbangkan :

  • Apa yang harus acquire()/ release()disebut? - [Bervariasi secara masif ]
  • Apakah kunci / semafor Anda menggunakan "antrian" atau "set" untuk mengingat utas menunggu?
  • Bisakah kunci / semafor Anda dibagikan dengan utas proses lainnya?
  • Apakah kunci Anda "reentrant"? - [Biasanya ya].
  • Apakah kunci Anda "memblokir / tidak menghalangi"? - [Biasanya non-blocking digunakan sebagai kunci blocking (alias spin-lock) menyebabkan kesibukan menunggu].
  • Bagaimana Anda memastikan operasi itu "atomik"?

Ini tergantung pada buku / dosen / bahasa / perpustakaan / lingkungan Anda.
Berikut ini adalah tur singkat yang mencatat bagaimana beberapa bahasa menjawab detail ini.


C, C ++ ( pthreads )

  • Sebuah mutex diimplementasikan melalui pthread_mutex_t. Secara default, mereka tidak dapat dibagi dengan proses lain ( PTHREAD_PROCESS_PRIVATE), namun mutex's memiliki atribut yang disebut berbagi . Saat disetel, maka mutex dibagi di antara proses ( PTHREAD_PROCESS_SHARED).
  • Sebuah kunci adalah hal yang sama seperti mutex.
  • Sebuah semaphore dilaksanakan melalui sem_t. Mirip dengan mutex, semaphore dapat dibagi antara tiga proses banyak atau dirahasiakan dengan utas dari satu proses tunggal. Ini tergantung pada argumen berbagi yang disediakan untuk sem_init.

python ( threading.py )

  • Sebuah kunci ( threading.RLock) sebagian besar sama dengan C / C ++ pthread_mutex_ts. Keduanya sama-sama reentrant . Ini berarti mereka hanya dapat dibuka oleh utas yang sama yang menguncinya. Ini adalah kasus bahwa sem_tsemaphore, threading.Semaphoresemaphores dan theading.Lockkunci tidak reentrant - karena itu adalah setiap thread dapat melakukan membuka kunci kunci / turun semaphore.
  • Sebuah mutex adalah sama sebagai kunci (istilah ini tidak digunakan sering dalam python).
  • Sebuah semaphore ( threading.Semaphore) sebagian besar sama dengan sem_t. Meskipun dengan sem_t, antrian id benang digunakan untuk mengingat urutan di mana utas menjadi diblokir ketika mencoba untuk menguncinya saat dikunci. Ketika utas membuka semafor, utas pertama dalam antrian (jika ada) dipilih untuk menjadi pemilik baru. Pengenal utas dihapus dari antrian dan semafor menjadi terkunci lagi. Namun, dengan threading.Semaphore, set digunakan sebagai ganti antrian, sehingga urutan di mana utas menjadi diblokir tidak disimpan - utas apa pun dalam set dapat dipilih untuk menjadi pemilik berikutnya.

Java ( java.util.concurrent )

  • Sebuah kunci ( java.util.concurrent.ReentrantLock) sebagian besar sama dengan C / C ++ pthread_mutex_t's, dan Python threading.RLockdalam hal itu juga menerapkan kunci reentrant. Berbagi kunci antar proses lebih sulit di Jawa karena JVM bertindak sebagai perantara. Jika utas mencoba membuka kunci kunci yang bukan miliknya, sebuahIllegalMonitorStateException dilemparkan.
  • Sebuah mutex adalah sama sebagai kunci (istilah ini tidak sering digunakan di Jawa).
  • Sebuah semaphore ( java.util.concurrent.Semaphore) sebagian besar sama dengan sem_tdan threading.Semaphore. Konstruktor untuk Java semaphores menerima parameter fairness boolean yang mengontrol apakah akan menggunakan set (false) atau antrian (true) untuk menyimpan utas menunggu.

Secara teori, semaphore sering dibahas, tetapi dalam praktiknya, semaphores tidak banyak digunakan. Semafor hanya menyimpan status satu bilangan bulat, sehingga seringkali agak tidak fleksibel dan banyak yang diperlukan sekaligus - menyebabkan kesulitan dalam memahami kode. Juga, fakta bahwa utas apa pun dapat melepaskan semaphore terkadang tidak diinginkan. Lebih banyak primitif / abstraksi sinkronisasi berorientasi objek / tingkat tinggi seperti "variabel kondisi" dan "monitor" yang digunakan.


22

Lihatlah Tutorial Multithreading oleh John Kopplin.

Di bagian Sinkronisasi Antar Utas , ia menjelaskan perbedaan antara acara, kunci, mutex, semaphore, penghitung waktu tunggu

Sebuah mutex dapat dimiliki oleh hanya satu thread pada satu waktu, yang memungkinkan benang untuk mengkoordinasikan akses eksklusif ke sumber daya bersama

Objek bagian kritis menyediakan sinkronisasi yang sama dengan yang disediakan oleh objek mutex, kecuali bahwa objek bagian kritis hanya dapat digunakan oleh utas proses tunggal

Perbedaan lain antara mutex dan bagian kritis adalah bahwa jika objek bagian kritis saat ini dimiliki oleh utas lain, EnterCriticalSection()menunggu tanpa batas waktu kepemilikan sedangkan WaitForSingleObject(), yang digunakan dengan mutex, memungkinkan Anda untuk menentukan batas waktu

Sebuah semaphore mempertahankan hitungan antara nol dan beberapa nilai maksimum, membatasi jumlah thread yang bersamaan mengakses sumber daya bersama.


15

Saya akan mencoba menutupinya dengan contoh:

Kunci: Salah satu contoh di mana Anda akan menggunakan lockkamus yang dibagikan di mana item (yang harus memiliki kunci unik) ditambahkan.
Kunci akan memastikan bahwa satu utas tidak memasukkan mekanisme kode yang memeriksa item dalam kamus, sedangkan utas lainnya (yang ada di bagian kritis) telah melewati pemeriksaan ini dan menambahkan item. Jika utas lain mencoba memasukkan kode yang dikunci, ia akan menunggu (diblokir) hingga objek dilepaskan.

private static readonly Object obj = new Object();

lock (obj) //after object is locked no thread can come in and insert item into dictionary on a different thread right before other thread passed the check...
{
    if (!sharedDict.ContainsKey(key))
    {
        sharedDict.Add(item);
    }
}

Semaphore: Katakanlah Anda memiliki kumpulan koneksi, maka utas tunggal mungkin mencadangkan satu elemen di kumpulan dengan menunggu semaphore untuk mendapatkan koneksi. Kemudian menggunakan koneksi dan ketika pekerjaan selesai melepaskan koneksi dengan merilis semaphore.

Contoh kode yang saya sukai adalah salah satu bouncer yang diberikan oleh @Patric - begini:

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace TheNightclub
{
    public class Program
    {
        public static Semaphore Bouncer { get; set; }

        public static void Main(string[] args)
        {
            // Create the semaphore with 3 slots, where 3 are available.
            Bouncer = new Semaphore(3, 3);

            // Open the nightclub.
            OpenNightclub();
        }

        public static void OpenNightclub()
        {
            for (int i = 1; i <= 50; i++)
            {
                // Let each guest enter on an own thread.
                Thread thread = new Thread(new ParameterizedThreadStart(Guest));
                thread.Start(i);
            }
        }

        public static void Guest(object args)
        {
            // Wait to enter the nightclub (a semaphore to be released).
            Console.WriteLine("Guest {0} is waiting to entering nightclub.", args);
            Bouncer.WaitOne();          

            // Do some dancing.
            Console.WriteLine("Guest {0} is doing some dancing.", args);
            Thread.Sleep(500);

            // Let one guest out (release one semaphore).
            Console.WriteLine("Guest {0} is leaving the nightclub.", args);
            Bouncer.Release(1);
        }
    }
}

Mutex Ini cukup banyak Semaphore(1,1)dan sering digunakan secara global (lebar aplikasi kalau tidak bisa dibilang locklebih tepat). Seseorang akan menggunakan global Mutexketika menghapus node dari daftar yang dapat diakses secara global (hal terakhir yang Anda ingin utas lainnya melakukan sesuatu saat Anda menghapus node). Ketika Anda memperoleh Mutexjika utas yang berbeda mencoba mengakuisisi yang sama, Mutexia akan ditidurkan hingga utas SAMA yang memperoleh Mutexpelepasannya.

Contoh yang bagus untuk membuat mutex global adalah oleh @deepee

class SingleGlobalInstance : IDisposable
{
    public bool hasHandle = false;
    Mutex mutex;

    private void InitMutex()
    {
        string appGuid = ((GuidAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(GuidAttribute), false).GetValue(0)).Value.ToString();
        string mutexId = string.Format("Global\\{{{0}}}", appGuid);
        mutex = new Mutex(false, mutexId);

        var allowEveryoneRule = new MutexAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), MutexRights.FullControl, AccessControlType.Allow);
        var securitySettings = new MutexSecurity();
        securitySettings.AddAccessRule(allowEveryoneRule);
        mutex.SetAccessControl(securitySettings);
    }

    public SingleGlobalInstance(int timeOut)
    {
        InitMutex();
        try
        {
            if(timeOut < 0)
                hasHandle = mutex.WaitOne(Timeout.Infinite, false);
            else
                hasHandle = mutex.WaitOne(timeOut, false);

            if (hasHandle == false)
                throw new TimeoutException("Timeout waiting for exclusive access on SingleInstance");
        }
        catch (AbandonedMutexException)
        {
            hasHandle = true;
        }
    }


    public void Dispose()
    {
        if (mutex != null)
        {
            if (hasHandle)
                mutex.ReleaseMutex();
            mutex.Dispose();
        }
    }
}

kemudian gunakan seperti:

using (new SingleGlobalInstance(1000)) //1000ms timeout on global lock
{
    //Only 1 of these runs at a time
    GlobalNodeList.Remove(node)
}

Semoga ini menghemat waktu Anda.


8

Wikipedia memiliki bagian yang bagus tentang perbedaan antara Semaphores dan Mutex :

Mutex pada dasarnya adalah hal yang sama dengan semaphore biner dan kadang-kadang menggunakan implementasi dasar yang sama. Perbedaan di antara mereka adalah:

Mutex memiliki konsep pemilik, yaitu proses yang mengunci mutex. Hanya proses yang mengunci mutex yang dapat membukanya. Sebaliknya, semafor tidak memiliki konsep pemilik. Setiap proses dapat membuka kunci semaphore.

Tidak seperti semaphore, mutex memberikan keamanan inversi prioritas. Karena mutex mengetahui pemiliknya saat ini, dimungkinkan untuk mempromosikan prioritas pemiliknya setiap kali tugas dengan prioritas lebih tinggi mulai menunggu di mutex.

Mutex juga memberikan keamanan penghapusan, di mana proses memegang mutex tidak dapat dihapus secara tidak sengaja. Semafor tidak menyediakan ini.


5

Pemahaman saya adalah bahwa mutex hanya untuk digunakan dalam satu proses tunggal, tetapi di banyak utasnya, sedangkan semafor dapat digunakan di berbagai proses, dan di seluruh rangkaian utasnya.

Juga, mutex adalah biner (baik terkunci atau tidak terkunci), sedangkan semaphore memiliki gagasan tentang penghitungan, atau antrian lebih dari satu permintaan kunci dan membuka kunci.

Bisakah seseorang memverifikasi penjelasan saya? Saya berbicara dalam konteks Linux, khususnya Red Hat Enterprise Linux (RHEL) versi 6, yang menggunakan kernel 2.6.32.


3
Sekarang ini mungkin berbeda di sistem operasi yang berbeda tetapi di windows Mutex dapat digunakan oleh beberapa proses setidaknya objek .net Mutex ..
Peter

2
stackoverflow.com/questions/9389730/... "Utas dalam proses yang sama atau dalam proses lain dapat berbagi mutex." jadi tidak ada mutex yang tidak boleh spesifik proses.
Peter

3

Menggunakan pemrograman C pada varian Linux sebagai contoh dasar untuk contoh.

Mengunci:

• Biasanya biner konstruksi yang sangat sederhana dalam operasi terkunci atau tidak terkunci

• Tidak ada konsep kepemilikan utas, prioritas, pengurutan dll.

• Biasanya kunci putaran di mana utas terus-menerus memeriksa ketersediaan kunci.

• Biasanya bergantung pada operasi atom misalnya Uji-dan-set, bandingkan-dan-tukar, ambil-dan-tambah dll.

• Biasanya memerlukan dukungan perangkat keras untuk operasi atom.

File Locks:

• Biasanya digunakan untuk mengoordinasikan akses ke file melalui berbagai proses.

• Beberapa proses dapat menahan kunci baca namun ketika proses tunggal menahan kunci tulis tidak ada proses lain yang diizinkan untuk mendapatkan kunci baca atau tulis.

• Contoh: kawanan, fcntl dll.

Mutex:

• Panggilan fungsi mutex biasanya bekerja di ruang kernel dan menghasilkan panggilan sistem.

• Menggunakan konsep kepemilikan. Hanya utas yang saat ini memegang mutex yang dapat membukanya.

• Mutex tidak bersifat rekursif (Pengecualian: PTHREAD_MUTEX_RECURSIVE).

• Biasanya digunakan dalam Asosiasi dengan Variabel Kondisi dan diteruskan sebagai argumen untuk misalnya pthread_cond_signal, pthread_cond_wait dll.

• Beberapa sistem UNIX memungkinkan mutex untuk digunakan oleh beberapa proses meskipun ini mungkin tidak diberlakukan pada semua sistem.

Tiang sinyal:

• Ini adalah integer yang dikelola kernel yang nilainya tidak diperbolehkan turun di bawah nol.

• Dapat digunakan untuk menyinkronkan proses.

• Nilai semaphore dapat diatur ke nilai yang lebih besar dari 1 dalam hal nilai biasanya menunjukkan jumlah sumber daya yang tersedia.

• Semafor yang nilainya terbatas pada 1 dan 0 disebut sebagai semaphore biner.


0

Supporting ownership, maximum number of processes share lockdan maximum number of allowed processes/threads in critical sectiontiga faktor utama yang menentukan nama / jenis objek bersamaan dengan nama umum lock. Karena nilai dari faktor-faktor ini adalah biner (memiliki dua status), kita dapat meringkasnya dalam tabel 3 * 8 seperti kebenaran.

  • X (Mendukung Kepemilikan?): Tidak (0) / ya (1)
  • Y (proses #sharing):> 1 (∞) / 1
  • Z (# proses / utas di CA):> 1 (∞) / 1

  X   Y   Z          Name
 --- --- --- ------------------------
  0   ∞   ∞   Semaphore              
  0   ∞   1   Binary Semaphore       
  0   1   ∞   SemaphoreSlim          
  0   1   1   Binary SemaphoreSlim(?)
  1   ∞   ∞   Recursive-Mutex(?)     
  1   ∞   1   Mutex                  
  1   1   ∞   N/A(?)                 
  1   1   1   Lock/Monitor           

Merasa bebas untuk mengedit atau memperluas tabel ini, saya telah mempostingnya sebagai tabel ascii untuk dapat diedit :)

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.