Apa arti threadsafe?

124

Baru-baru ini saya mencoba mengakses kotak teks dari utas (selain utas UI) dan pengecualian dilemparkan. Ia mengatakan sesuatu tentang "kode tidak menjadi thread aman" dan akhirnya saya menulis sebuah delegasi (sampel dari MSDN membantu) dan memanggilnya sebagai gantinya.

Tapi meski begitu saya tidak begitu mengerti mengapa semua kode tambahan itu diperlukan.

Pembaruan: Apakah saya akan mengalami masalah serius jika saya memeriksanya

Controls.CheckForIllegalCrossThread..blah =true
Vivek Bernard
sumber
5
Biasanya, "thread safe" berarti apa pun yang dipikirkan orang yang menggunakan istilah itu, setidaknya bagi orang itu. Karena itu, ini bukanlah konstruksi bahasa yang sangat berguna - Anda harus lebih spesifik ketika berbicara tentang perilaku kode berulir.
@ Dave Maaf saya mencoba mencari, tapi menyerah ... terima kasih ..
Vivek Bernard
1
sebuah kode yang tidak munculRace-Condition
Muhammad Babar

Jawaban:

121

Eric Lippert memiliki posting blog yang bagus berjudul Apa yang Anda sebut "thread safe"?tentang definisi keamanan benang seperti yang ditemukan di Wikipedia.

3 hal penting yang diambil dari tautan:

“Sepotong kode aman untuk thread jika berfungsi dengan benar selama eksekusi simultan oleh beberapa thread.”

“Secara khusus, ini harus memenuhi kebutuhan beberapa utas untuk mengakses data bersama yang sama,…”

“... dan kebutuhan akan bagian data yang dibagikan untuk diakses hanya oleh satu utas pada waktu tertentu.”

Layak dibaca!

Gregory Pakosz
sumber
25
Harap hindari jawaban hanya tautan karena dapat menjadi buruk kapan saja di masa mendatang.
akhil_mittal
1
tautan yang diperbarui: docs.microsoft.com/en-nz/archive/blogs/ericlippert/…
Ryan Buddicom
106

Dalam istilah yang paling sederhana, threadsafe berarti aman untuk diakses dari banyak thread. Saat Anda menggunakan beberapa utas dalam sebuah program dan mereka masing-masing mencoba mengakses struktur data atau lokasi umum dalam memori, beberapa hal buruk dapat terjadi. Jadi, Anda menambahkan beberapa kode tambahan untuk mencegah hal-hal buruk tersebut. Misalnya, jika dua orang menulis dokumen yang sama pada saat yang sama, orang kedua yang akan menyimpan akan menimpa pekerjaan orang pertama. Untuk membuatnya aman utas, Anda harus memaksa orang 2 untuk menunggu orang 1 menyelesaikan tugas mereka sebelum mengizinkan orang 2 untuk mengedit dokumen.

Vincent Ramdhanie
sumber
11
Ini disebut sinkronisasi. Baik?
JavaTechnical
3
Iya. Memaksa berbagai utas menunggu akses ke sumber daya bersama dapat diselesaikan dengan sinkronisasi.
Vincent Ramdhanie
Dari jawaban yang diterima Gregory, dia mengatakan "" Sepotong kode aman untuk thread jika berfungsi dengan benar selama eksekusi simultan oleh beberapa thread ". sementara Anda mengatakan "Untuk membuatnya aman, Anda harus memaksa orang 1 untuk menunggu"; bukankah dia mengatakan simultan dapat diterima sementara Anda mengatakan itu tidak? Bisakah Anda jelaskan?
Honey
Itu adalah hal yang sama. Saya hanya menyarankan mekanisme sederhana sebagai contoh tentang apa yang membuat kode aman untuk thread. terlepas dari mekanisme yang digunakan meskipun beberapa utas yang menjalankan kode yang sama tidak boleh saling mengganggu.
Vincent Ramdhanie
Jadi apakah ini hanya berlaku untuk kode yang menggunakan variabel global dan statis? Menggunakan contoh orang yang mengedit dokumen, saya rasa tidak masuk akal untuk mencegah orang 2 menjalankan kode penulisan dokumen pada dokumen lain.
Aaron Franke
18

Wikipedia memiliki artikel tentang Keamanan Benang.

Halaman definisi ini (Anda harus melewati iklan - maaf) mendefinisikannya sebagai berikut:

Dalam pemrograman komputer, thread-safe menjelaskan bagian program atau rutinitas yang dapat dipanggil dari beberapa thread pemrograman tanpa interaksi yang tidak diinginkan antara thread tersebut.

Utas adalah jalur eksekusi suatu program. Program utas tunggal hanya akan memiliki satu utas dan jadi masalah ini tidak muncul. Hampir semua program GUI memiliki beberapa jalur eksekusi dan karenanya utas - setidaknya ada dua, satu untuk memproses tampilan GUI dan memberikan masukan pengguna, dan setidaknya satu lagi untuk benar-benar melakukan operasi program.

Ini dilakukan agar UI tetap responsif saat program bekerja dengan memindahkan proses yang berjalan lama ke thread non-UI. Utas ini dapat dibuat satu kali dan ada selama program berlangsung, atau dibuat begitu saja saat diperlukan dan dihancurkan setelah selesai.

Karena utas ini sering kali perlu melakukan tindakan umum - disk i / o, menampilkan hasil ke layar, dll. - bagian kode ini perlu ditulis sedemikian rupa sehingga dapat menangani panggilan dari beberapa utas, sering kali di waktu yang sama. Ini akan melibatkan hal-hal seperti:

  • Mengerjakan salinan data
  • Menambahkan kunci di sekitar kode kritis
ChrisF
sumber
8

Sederhananya, thread safe berarti metode atau instance kelas dapat digunakan oleh beberapa thread secara bersamaan tanpa terjadi masalah.

Pertimbangkan metode berikut:

private int myInt = 0;
public int AddOne()
{
    int tmp = myInt;
    tmp = tmp + 1;
    myInt = tmp;
    return tmp;
}

Sekarang utas A dan utas B keduanya ingin menjalankan AddOne (). tetapi A memulai terlebih dahulu dan membaca nilai myInt (0) menjadi tmp. Sekarang untuk beberapa alasan penjadwal memutuskan untuk menghentikan thread A dan menunda eksekusi ke thread B. Thread B sekarang juga membaca nilai myInt (masih 0) ke dalam variabel tmp itu sendiri. Thread B menyelesaikan seluruh metode, jadi pada akhirnya myInt = 1. Dan 1 dikembalikan. Sekarang giliran Thread A lagi. Thread A berlanjut. Dan menambahkan 1 ke tmp (tmp adalah 0 untuk utas A). Dan kemudian simpan nilai ini di myInt. myInt lagi 1.

Jadi dalam hal ini metode AddOne dipanggil dua kali, tetapi karena metode tersebut tidak diimplementasikan dengan cara yang aman untuk thread, nilai myInt bukan 2, seperti yang diharapkan, tetapi 1 karena thread kedua membaca variabel myInt sebelum thread pertama selesai memperbaruinya.

Membuat metode aman untuk benang sangat sulit dalam kasus yang tidak sepele. Dan ada beberapa teknik. Di Java, Anda dapat menandai metode sebagai tersinkronisasi, ini berarti hanya satu thread yang dapat mengeksekusi metode tersebut pada waktu tertentu. Utas lainnya menunggu dalam antrean. Ini membuat thread metode aman, tetapi jika ada banyak pekerjaan yang harus diselesaikan dalam sebuah metode, maka ini akan membuang banyak ruang. Teknik lainnya adalah 'menandai hanya sebagian kecil dari suatu metode sebagai tersinkronisasi'dengan membuat kunci atau semaphore, dan mengunci bagian kecil ini (biasa disebut bagian kritis). Bahkan ada beberapa metode yang diimplementasikan sebagai pengaman utas tanpa kunci, yang berarti bahwa metode itu dibuat sedemikian rupa sehingga banyak utas dapat membalap melalui mereka pada saat yang sama tanpa pernah menimbulkan masalah, ini dapat terjadi jika metode hanya menjalankan satu panggilan atom. Panggilan atom adalah panggilan yang tidak dapat diganggu dan hanya dapat dilakukan oleh satu utas dalam satu waktu.

Sujith PS
sumber
jika metode AddOne dipanggil dua kali
Sujith PS
7

Dalam dunia nyata contoh bagi orang awam adalah

Misalkan Anda memiliki rekening bank dengan internet dan mobile banking dan akun Anda hanya memiliki $ 10. Anda melakukan transfer saldo ke rekening lain menggunakan mobile banking, sementara itu, Anda melakukan belanja online menggunakan rekening bank yang sama. Jika rekening bank ini bukan threadsafe, maka bank mengizinkan Anda untuk melakukan dua transaksi pada saat yang sama dan kemudian bank tersebut akan bangkrut.

Threadsafe berarti bahwa status objek tidak berubah jika beberapa thread mencoba mengakses objek secara bersamaan.

Yasir Shabbir Choudhary
sumber
5

Anda bisa mendapatkan penjelasan lebih lanjut dari buku "Java Concurrency in Practice":

Sebuah kelas aman untuk thread jika berperilaku dengan benar saat diakses dari beberapa thread, terlepas dari penjadwalan atau interleaving eksekusi thread tersebut oleh lingkungan waktu proses, dan tanpa sinkronisasi tambahan atau koordinasi lain dari bagian kode panggilan.

Jacky
sumber
4

Sebuah modul aman untuk thread jika ia menjamin dapat mempertahankan invariannya dalam menghadapi penggunaan multi-thread dan penggunaan bersamaan.

Di sini, modul dapat berupa struktur data, kelas, objek, metode / prosedur, atau fungsi. Pada dasarnya potongan kode dan data terkait.

Jaminan berpotensi dibatasi pada lingkungan tertentu seperti arsitektur CPU tertentu, tetapi harus berlaku untuk lingkungan tersebut. Jika tidak ada batasan eksplisit lingkungan, maka biasanya dianggap menyiratkan bahwa itu berlaku untuk semua lingkungan yang kode dapat dikompilasi dan dijalankan.

Modul thread-unsafe dapat berfungsi dengan benar di bawah penggunaan mutli-threaded dan bersamaan, tetapi ini seringkali lebih karena keberuntungan dan kebetulan, daripada desain yang cermat. Meskipun beberapa modul tidak rusak untuk Anda di bawah, itu mungkin rusak saat dipindahkan ke lingkungan lain.

Bug multi-threading seringkali sulit untuk di-debug. Beberapa di antaranya hanya terjadi sesekali, sementara yang lain terwujud secara agresif - ini juga, dapat menjadi spesifik lingkungan. Mereka bisa bermanifestasi sebagai hasil yang salah secara halus, atau jalan buntu. Mereka dapat mengacaukan struktur data dengan cara yang tidak dapat diprediksi, dan menyebabkan bug lain yang tampaknya tidak mungkin muncul di bagian kode yang jauh lainnya. Ini bisa sangat spesifik untuk aplikasi, jadi sulit untuk memberikan gambaran umum.

Chris Vest
sumber
3

Keamanan benang: Program aman benang melindungi datanya dari kesalahan konsistensi memori. Dalam program yang sangat multi-utas, program aman utas tidak menyebabkan efek samping apa pun dengan beberapa operasi baca / tulis dari beberapa utas pada objek yang sama. Utas yang berbeda dapat membagikan dan mengubah data objek tanpa kesalahan konsistensi.

Anda dapat mencapai keamanan thread dengan menggunakan API konkurensi lanjutan. Halaman dokumentasi ini menyediakan konstruksi pemrograman yang baik untuk mencapai keamanan thread.

Lock Objects mendukung idiom penguncian yang menyederhanakan banyak aplikasi bersamaan.

Pelaksana mendefinisikan API tingkat tinggi untuk meluncurkan dan mengelola utas. Implementasi pelaksana yang disediakan oleh java.util.concurrent menyediakan manajemen kumpulan utas yang sesuai untuk aplikasi berskala besar.

Koleksi Bersamaan memudahkan pengelolaan koleksi data yang besar, dan dapat sangat mengurangi kebutuhan sinkronisasi.

Variabel Atom memiliki fitur yang meminimalkan sinkronisasi dan membantu menghindari kesalahan konsistensi memori.

ThreadLocalRandom (di JDK 7) menyediakan pembuatan nomor pseudorandom yang efisien dari beberapa utas.

Mengacu pada java.util.concurrent dan java.util.concurrent.atomic paket juga untuk konstruksi pemrograman lainnya.

Ravindra babu
sumber
1

Anda jelas bekerja di lingkungan WinForms. WinForms mengontrol afinitas utas pameran, yang berarti utas tempat mereka dibuat adalah satu-satunya utas yang dapat digunakan untuk mengakses dan memperbaruinya. Itulah mengapa Anda akan menemukan contoh di MSDN dan di tempat lain yang mendemonstrasikan cara mengatur panggilan kembali ke utas utama.

Praktik WinForms normal adalah memiliki satu utas yang didedikasikan untuk semua pekerjaan UI Anda.

David M
sumber
1

Saya menemukan konsep http://en.wikipedia.org/wiki/Reentrancy_%28computing%29 menjadi apa yang biasanya saya anggap sebagai threading tidak aman yaitu ketika sebuah metode memiliki dan bergantung pada efek samping seperti variabel global.

Misalnya saya telah melihat kode yang diformat angka floating point menjadi string, jika dua di antaranya dijalankan di utas yang berbeda, nilai global decimalSeparator dapat diubah secara permanen menjadi '.'

//built in global set to locale specific value (here a comma)
decimalSeparator = ','

function FormatDot(value : real):
    //save the current decimal character
    temp = decimalSeparator

    //set the global value to be 
    decimalSeparator = '.'

    //format() uses decimalSeparator behind the scenes
    result = format(value)

    //Put the original value back
    decimalSeparator = temp
Aaron Robson
sumber
-2

Untuk memahami keamanan benang, baca bagian di bawah ini :

4.3.1. Contoh: Vehicle Tracker Menggunakan Delegasi

Sebagai contoh delegasi yang lebih substansial, mari kita buat versi pelacak kendaraan yang mendelegasikan ke kelas aman-thread. Kami menyimpan lokasi di Peta, jadi kami mulai dengan implementasi Peta yang aman untuk thread ConcurrentHashMap,. Kami juga menyimpan lokasi menggunakan kelas Point yang tidak dapat diubah, bukan seperti yang MutablePointditunjukkan pada Listing 4.6.

Daftar 4.6. Kelas Immutable Point yang digunakan oleh DelegatingVehicleTracker.

 class Point{
  public final int x, y;

  public Point() {
        this.x=0; this.y=0;
    }

  public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

}

Pointaman untuk thread karena tidak dapat diubah. Nilai yang tidak dapat diubah dapat dibagikan dan dipublikasikan dengan bebas, jadi kami tidak perlu lagi menyalin lokasi saat mengembalikannya.

DelegatingVehicleTrackerpada Listing 4.7 tidak menggunakan sinkronisasi eksplisit; semua akses ke status dikelola oleh ConcurrentHashMap, dan semua kunci serta nilai Peta tidak dapat diubah.

Daftar 4.7. Mendelegasikan Keamanan Thread ke ConcurrentHashMap.

  public class DelegatingVehicleTracker {

  private final ConcurrentMap<String, Point> locations;
    private final Map<String, Point> unmodifiableMap;

  public DelegatingVehicleTracker(Map<String, Point> points) {
        this.locations = new ConcurrentHashMap<String, Point>(points);
        this.unmodifiableMap = Collections.unmodifiableMap(locations);
    }

  public Map<String, Point> getLocations(){
        return this.unmodifiableMap; // User cannot update point(x,y) as Point is immutable
    }

  public Point getLocation(String id) {
        return locations.get(id);
    }

  public void setLocation(String id, int x, int y) {
        if(locations.replace(id, new Point(x, y)) == null) {
             throw new IllegalArgumentException("invalid vehicle name: " + id);
        }
    }

}

Jika kita menggunakan MutablePointkelas asli dan bukan Point, kita akan merusak enkapsulasi dengan membiarkan getLocationsmempublikasikan referensi ke keadaan yang bisa berubah yang tidak aman untuk thread. Perhatikan bahwa kami telah mengubah sedikit perilaku kelas pelacak kendaraan; sementara versi monitor mengembalikan cuplikan lokasi, versi yang mendelegasikan mengembalikan tampilan lokasi kendaraan yang tidak dapat dimodifikasi tetapi "langsung". Ini berarti bahwa jika utas A memanggil getLocationsdan utas B kemudian mengubah lokasi beberapa titik, perubahan itu tercermin dalam Peta yang dikembalikan ke utas A.

4.3.2. Variabel Status Independen

Kami juga dapat mendelegasikan keamanan thread ke lebih dari satu variabel status yang mendasari selama variabel status yang mendasarinya adalah independen, yang berarti bahwa kelas komposit tidak menerapkan invarian yang melibatkan beberapa variabel status.

VisualComponentpada Listing 4.9 adalah komponen grafis yang memungkinkan klien untuk mendaftarkan listener untuk kejadian tetikus dan penekanan tombol. Ia memelihara daftar pendengar terdaftar dari setiap jenis, sehingga ketika sebuah peristiwa terjadi pendengar yang sesuai dapat dipanggil. Tetapi tidak ada hubungan antara kumpulan pendengar mouse dan pendengar kunci; keduanya independen, dan oleh karena itu VisualComponentdapat mendelegasikan kewajiban keamanan utasnya ke dua daftar utas aman yang mendasarinya.

Daftar 4.9. Mendelegasikan Keamanan Thread ke Beberapa Variabel Status Dasar.

public class VisualComponent {
    private final List<KeyListener> keyListeners 
                                        = new CopyOnWriteArrayList<KeyListener>();
    private final List<MouseListener> mouseListeners 
                                        = new CopyOnWriteArrayList<MouseListener>();

  public void addKeyListener(KeyListener listener) {
        keyListeners.add(listener);
    }

  public void addMouseListener(MouseListener listener) {
        mouseListeners.add(listener);
    }

  public void removeKeyListener(KeyListener listener) {
        keyListeners.remove(listener);
    }

  public void removeMouseListener(MouseListener listener) {
        mouseListeners.remove(listener);
    }

}

VisualComponentmenggunakan CopyOnWriteArrayListuntuk menyimpan setiap daftar pendengar; ini adalah implementasi Daftar thread-safe yang sangat cocok untuk mengelola daftar pendengar (lihat Bagian 5.2.3). Setiap Daftar aman untuk thread, dan karena tidak ada batasan yang menggabungkan status satu ke status lainnya, VisualComponentdapat mendelegasikan tanggung jawab keamanan utasnya ke objek mouseListenersdan yang mendasarinya keyListeners.

4.3.3. Saat Delegasi Gagal

Kebanyakan kelas komposit tidak sesederhana VisualComponent: mereka memiliki invarian yang menghubungkan variabel status komponennya. NumberRangepada Listing 4.10 menggunakan dua AtomicIntegersuntuk mengatur statusnya, tetapi memberikan batasan tambahan — bahwa angka pertama kurang dari atau sama dengan yang kedua.

Daftar 4.10. Kelas Rentang Angka yang Tidak Cukup Melindungi Invariannya. Jangan lakukan ini.

public class NumberRange {

  // INVARIANT: lower <= upper
    private final AtomicInteger lower = new AtomicInteger(0);
    private final AtomicInteger upper = new AtomicInteger(0);

  public void setLower(int i) {
        //Warning - unsafe check-then-act
        if(i > upper.get()) {
            throw new IllegalArgumentException(
                    "Can't set lower to " + i + " > upper ");
        }
        lower.set(i);
    }

  public void setUpper(int i) {
        //Warning - unsafe check-then-act
        if(i < lower.get()) {
            throw new IllegalArgumentException(
                    "Can't set upper to " + i + " < lower ");
        }
        upper.set(i);
    }

  public boolean isInRange(int i){
        return (i >= lower.get() && i <= upper.get());
    }

}

NumberRangeadalah tidak benang-aman ; ia tidak mempertahankan invarian yang membatasi bagian bawah dan atas. Metode setLowerdan setUpperberusaha untuk menghormati invarian ini, tetapi melakukannya dengan buruk. Keduanya setLowerdan setUppermerupakan urutan check-then-act, tetapi tidak menggunakan penguncian yang memadai untuk menjadikannya atomik. Jika rentang nomor menahan (0, 10), dan satu utas memanggil setLower(5)sementara utas lainnya memanggil setUpper(4), dengan beberapa pengaturan waktu yang tidak menguntungkan keduanya akan lulus pemeriksaan di penyetel dan kedua modifikasi akan diterapkan. Hasilnya adalah bahwa kisaran sekarang memegang (5, 4) - status tidak valid . Jadi, meskipun AtomicIntegers yang mendasari aman untuk thread, kelas kompositnya tidak . Karena variabel status yang mendasari lowerdanupper tidak independen,NumberRange tidak bisa begitu saja mendelegasikan keamanan thread ke variabel status aman thread-nya.

NumberRangedapat dibuat thread-safe dengan menggunakan penguncian untuk mempertahankan invariannya, seperti menjaga bagian bawah dan atas dengan kunci umum. Itu juga harus menghindari penerbitan bawah dan atas untuk mencegah klien menumbangkan invariannya.

Jika sebuah kelas memiliki tindakan gabungan, seperti NumberRangehalnya, pendelegasian saja bukanlah pendekatan yang sesuai untuk keamanan thread. Dalam kasus ini, kelas harus menyediakan pengunciannya sendiri untuk memastikan bahwa tindakan gabungan bersifat atomik, kecuali seluruh tindakan gabungan juga dapat didelegasikan ke variabel status yang mendasarinya.

Jika sebuah kelas terdiri dari beberapa variabel status aman thread independen dan tidak memiliki operasi yang memiliki transisi status tidak valid, maka kelas tersebut dapat mendelegasikan keamanan thread ke variabel status yang mendasarinya.

pertukaran berlebih
sumber