থ্রেড সুরক্ষা তালিকা


91

আমি বুঝতে পারি যে সাধারণভাবে একটি তালিকা থ্রেড নিরাপদ নয়, তবে থ্রেডগুলি তালিকায় কোনও অন্য ক্রিয়াকলাপটি কখনই সম্পাদন করে না (যেমন এটি অনুসরণ করা) তবে কেবল তালিকায় আইটেম যুক্ত করার কোনও ভুল আছে কি?

উদাহরণ:

List<object> list = new List<object>();
Parallel.ForEach(transactions, tran =>
{
    list.Add(new object());
});


আমি একবারে তালিকাগুলি <T> কেবলমাত্র সমান্তরালভাবে চালিত একাধিক কার্য থেকে নতুন অবজেক্ট যুক্ত করতে ব্যবহার করেছি। কখনও কখনও, খুব বিরল, সমস্ত কাজ সমাপ্ত হওয়ার পরে তালিকার মধ্য দিয়ে পুনরাবৃত্তি করার সময় এটি একটি রেকর্ড দিয়ে শেষ হয় যা নাল ছিল, যা কোনও অতিরিক্ত থ্রেড জড়িত না থাকলে এটি ঘটানো কার্যত অসম্ভব হত। আমার ধারণা, যখন তালিকাটি অভ্যন্তরীণভাবে তার উপাদানগুলিকে প্রসারিত করার জন্য পুনরায় বরাদ্দ করছিল, তখন অন্য কোনও থ্রেড অন্য কোনও অবজেক্ট যুক্ত করার চেষ্টা করে এটিকে গোলমাল করেছিল। সুতরাং এটি করা ভাল ধারণা না!
— ওসমিউবিন

অবৈধভাবে আমি একাধিক থ্রেড থেকে যোগ করার সময় অবাস্তবভাবে শূন্য হয়ে যাওয়ার বিষয়ে @osmiumbin কে বর্তমানে দেখতে পাচ্ছি নিশ্চিত করার জন্য ধন্যবাদ.
— ব্ল্যাককি

উত্তর:


75

পর্দার আড়ালে বাফারগুলি পুনরায় প্রকাশ করা এবং উপাদানগুলির অনুলিপি সহ অনেক কিছু ঘটে। এই কোড বিপদ সৃষ্টি করবে। খুব সহজভাবে, কোনও তালিকায় যুক্ত করার সময় কোনও পারমাণবিক ক্রিয়াকলাপ নেই, কমপক্ষে "দৈর্ঘ্য" বৈশিষ্ট্যটির আপডেট হওয়া দরকার, এবং আইটেমটি সঠিক স্থানে স্থাপন করা দরকার, এবং (যদি কোনও পৃথক পরিবর্তনশীল থাকে) সূচকের প্রয়োজন হয় আপডেট করা. একাধিক থ্রেড একে অপরকে পদদলিত করতে পারে। এবং যদি একটি বৃদ্ধি প্রয়োজন হয় তবে আরও অনেক কিছু চলছে। যদি কোনও লিস্টে কিছু লেখা থাকে তবে অন্য কিছু পড়তে বা লিখতে হবে না।

.NET 4.0-এ আমাদের একযোগে সংগ্রহ রয়েছে, যা হ্যান্ডলি থ্রেডসেফ এবং লকগুলির প্রয়োজন নেই।


এটি নিখুঁতভাবে উপলব্ধি করে, আমি অবশ্যই এটির জন্য নতুন সমকালীন সংগ্রহগুলিতে লক্ষ্য করব। ধন্যবাদ.
— e36M3

11
মনে রাখবেন যে কোনও বিল্ট-ইন ConcurrentListটাইপ নেই। সমবর্তী ব্যাগ, অভিধান, স্ট্যাক, সারি ইত্যাদি রয়েছে তবে কোনও তালিকা নেই।
— লুক এইচ

11

আপনার বর্তমান পদ্ধতির থ্রেড-নিরাপদ নয় - আমি এটি পুরোপুরি এড়িয়ে চলার পরামর্শ দেব - যেহেতু আপনি মূলত কোনও ডেটা ট্রান্সফর্মেশন করেন পিএনএলকিউ আরও ভাল পদ্ধতির হতে পারে (আমি জানি এটি একটি সরলিকৃত উদাহরণ তবে শেষ পর্যন্ত আপনি প্রতিটি লেনদেনকে অন্য "রাজ্যে রূপান্তর করছেন" "অবজেক্ট)।

List<object> list = transactions.AsParallel()
                                .Select( tran => new object())
                                .ToList();

আমি লিস্টের দিকটি সম্পর্কে জোর দেওয়ার জন্য একটি অতি সরল উদাহরণ উপস্থাপন করেছি I আমি আগ্রহী ছিলাম তা যুক্ত করুন My আমার প্যারালাল ore ফোর্যাচ বাস্তবে ভাল পরিমাণে কাজ করবে এবং একটি সহজ উপাত্ত রূপান্তর হবে না। ধন্যবাদ
— e36M3

4
অবিকৃত ব্যবহার করা হলে সমবর্তী সংগ্রহগুলি আপনার সমান্তরাল পারফরম্যান্সকে পঙ্গু করে দিতে পারে - আপনি করতে পারেন অন্য একটি জিনিস একটি স্থির আকারের অ্যারে ব্যবহার করে এবং Parallel.Foreachসূচকগুলিতে নেওয়া ওভারলোডটি ব্যবহার করে - সেই ক্ষেত্রে প্রতিটি থ্রেড আলাদা অ্যারে প্রবেশের কৌশলটি পরিচালনা করে এবং আপনার নিরাপদ থাকা উচিত।
— ব্রোকেনগ্লাস

6

এটি জিজ্ঞাসা করা অযৌক্তিক জিনিস নয়। সেখানে হয় ক্ষেত্রেই যদি তারা শুধুমাত্র পদ্ধতি বলা হয় যেখানে পদ্ধতি যা অন্যান্য পদ্ধতি সঙ্গে একযোগে থ্রেড-নিরাপত্তা বিষয় সৃষ্টি করতে পারে নিরাপদ।

যাইহোক, আপনি স্পষ্টকগুলিতে প্রদর্শিত কোডটি বিবেচনা করার সময় এটি স্পষ্টতই এটির একটি ঘটনা নয়:

public void Add(T item)
{
    if (this._size == this._items.Length)
    {
        this.EnsureCapacity(this._size + 1);
    }
    this._items[this._size++] = item;
    this._version++;
}

যদিও EnsureCapacity থ্রেডসেফ নিজে থেকেই ছিল (এবং এটি অবশ্যই নাও হয়), উপরোক্ত কোডটি স্পষ্টতই থ্রেডসেফ হতে যাচ্ছে না, বর্ধিত অপারেটরের এক সাথে কল করার সম্ভাবনা বিবেচনা করে ভুল লেখায়।

হয় লক করুন, কনক্রন্টলিস্ট ব্যবহার করুন, বা সম্ভবত একাধিক থ্রেড যে জায়গায় লিখেছেন তা লক-ফ্রি সারি ব্যবহার করুন এবং সেখান থেকে পঠন করুন - হয় সরাসরি বা এটির সাথে একটি তালিকা পূরণ করে - তারা তাদের কাজ শেষ করার পরে (আমি ধরে নিচ্ছি যে একক থ্রেডযুক্ত পাঠের পরে একাধিক একযোগে লেখাগুলি এখানে আপনার প্যাটার্ন, আপনার প্রশ্ন থেকে বিচার করা, অন্যথায় আমি দেখতে পাচ্ছি না যেখানে Addকেবলমাত্র একমাত্র পদ্ধতি বলা শর্তটি কোনওভাবেই কার্যকর হতে পারে)।


6

আপনি যদি List.addএকাধিক থ্রেড থেকে ব্যবহার করতে চান এবং ক্রমটির বিষয়ে চিন্তা করেন না, তবে সম্ভবত আপনার Listকোনও উপায়ে সূচকের দক্ষতার প্রয়োজন হবে না এবং পরিবর্তে উপলভ্য কয়েকটি সমবর্তী সংগ্রহগুলি ব্যবহার করা উচিত।

আপনি যদি এই পরামর্শটিকে উপেক্ষা করে এবং কেবল এটি করেন তবে আপনি থ্রেডটি নিরাপদ করে addতুলতে পারেন addতবে এটি অনির্দেশ্য ক্রমে:

private Object someListLock = new Object(); // only once

...

lock (someListLock)
{
    someList.Add(item);
}

আপনি এই অনিশ্চিত ক্রম গ্রহণ করেন, সম্ভাবনা যে আপনি আগে যেমন উল্লেখ করা একটি সংগ্রহ যে হিসাবে ইন্ডেক্স করতে পারি না প্রয়োজন হবে না হয় someList[i]।


5

এটি সমস্যার কারণ হতে পারে যেহেতু তালিকাটি অ্যারের উপরে নির্মিত হয়েছে এবং থ্রেড নিরাপদ নয় আপনি থ্রেডগুলি কোথায় রয়েছে তার উপর নির্ভর করে সীমার ব্যতিক্রম বা অন্য মানগুলিকে ওভাররাইড করে কিছু মান খুঁজে পেতে পারেন। মূলত, এটি করবেন না।

একাধিক সম্ভাব্য সমস্যা আছে ... ঠিক নেই। আপনার যদি কোনও থ্রেড নিরাপদ সংগ্রহের প্রয়োজন হয় তবে হয় একটি লক ব্যবহার করুন বা সিস্টেমের মধ্যে একটি। সংগ্রহ on সমকালীন সংগ্রহ।


5

আমি এটির ConcurrentBag<T>পরিবর্তে ব্যবহার করে আমার সমস্যার সমাধান করেছি List<T>:

ConcurrentBag<object> list = new ConcurrentBag<object>();
Parallel.ForEach(transactions, tran =>
{
    list.Add(new object());
});

2

থ্রেডগুলি কখনই তালিকায় অন্য কোনও ক্রিয়াকলাপ না করে যদি কেবল তালিকায় আইটেম যুক্ত করে কিছু ভুল হয়?

সংক্ষিপ্ত উত্তর: হ্যাঁ

দীর্ঘ উত্তর: নীচের প্রোগ্রামটি চালান।

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;

class Program
{
    readonly List<int> l = new List<int>();
    const int amount = 1000;
    int toFinish = amount;
    readonly AutoResetEvent are = new AutoResetEvent(false);

    static void Main()
    {
        new Program().Run();
    }

    void Run()
    {
        for (int i = 0; i < amount; i++)
            new Thread(AddTol).Start(i);

        are.WaitOne();

        if (l.Count != amount ||
            l.Distinct().Count() != amount ||
            l.Min() < 0 ||
            l.Max() >= amount)
            throw new Exception("omg corrupted data");

        Console.WriteLine("All good");
        Console.ReadKey();
    }

    void AddTol(object o)
    {
        // uncomment to fix
        // lock (l) 
        l.Add((int)o);

        int i = Interlocked.Decrement(ref toFinish);

        if (i == 0)
            are.Set();
    }
}

@ রোয়ি আপনি কি এটি একক কোর মেশিনে চালাচ্ছেন?
— বাস স্মিট

হাই, আমি মনে করি যে এই উদাহরণটিতে কোনও সমস্যা আছে কারণ এটি যখনই 1000 নম্বর পেয়ে যায় অটোআরসেটভেন্টটি সেট করে। কারণ এটি এই থ্রেডগুলিকে প্রক্রিয়া করতে পারে যখনই এটি চাইবে এটি 1000 এ পৌঁছানোর আগে এটি 999 এ যাওয়ার আগে এটি 1000 এ পৌঁছে যাবে। আপনি যদি অ্যাডটল পদ্ধতিতে একটি কনসোল.উইরলটাইন যুক্ত করেন তবে আপনি দেখতে পাবেন যে নম্বরটি ক্রমযুক্ত নয়।
— ডেভ ওয়াকার

@ ডেভ, এর ইভেন্টটি সেটিং করুন যখন আমি == 0
— বাস স্মিথ

2

অন্যরা ইতিমধ্যে বলেছে, আপনি এর কাছ থেকে সংগ্রহগুলি ব্যবহার করতে পারেন System.Collections.Concurrent নামের স্থান । আপনি যদি এর মধ্যে একটি ব্যবহার করতে পারেন তবে এটি পছন্দনীয়।

তবে যদি আপনি সত্যিই একটি তালিকা চান যা কেবলমাত্র সিঙ্ক্রোনাইজ করা হয় তবে আপনি SynchronizedCollection<T>ক্লাস-এ দেখতে পারেন System.Collections.Generic।

নোট করুন যে আপনাকে সিস্টেম.সেসওয়ারমোডেল অ্যাসেমব্লিকে অন্তর্ভুক্ত করতে হয়েছিল, এটিও কারণ যে আমি এটি এত পছন্দ করি না। তবে মাঝে মাঝে আমি এটি ব্যবহার করি।


আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.