রুবিতে থ্রেড-সেফ কী নেই তা কীভাবে জানবেন?


93

রেল 4 থেকে শুরু করে , সমস্ত কিছুই ডিফল্টরূপে থ্রেডযুক্ত পরিবেশে চলতে হবে। এর অর্থ কী আমরা লিখি সমস্ত কোড এবং আমরা ব্যবহার করি সমস্ত রত্ন হওয়া প্রয়োজনthreadsafe

সুতরাং, আমার এ সম্পর্কে কয়েকটি প্রশ্ন রয়েছে:

  1. রুবি / রেলগুলিতে থ্রেড-সেফ কী নয়? বনাম রুবি / রেলগুলিতে থ্রেড-নিরাপদ কী?
  2. সেখানে রত্ন যে একটি তালিকা রয়েছে হয় বা threadsafe হিসেবে পরিচিত ভাইস বিপরীতভাবে?
  3. থ্রেডসেইফ উদাহরণ নয় এমন কোডের সাধারণ প্যাটার্নগুলির তালিকা রয়েছে @result ||= some_method?
  4. রুবি ল্যাং কোরের ডেটা স্ট্রাকচারগুলি যেমন Hashথ্রেডসেফের মতো?
  5. এমআরআইতে, যেখানে একটি GVL/GIL যার অর্থ কেবল 1 টি রুবি থ্রেড বাদে IOএকসাথে চলতে পারে, থ্রেডস্যাফ পরিবর্তন আমাদের কী প্রভাবিত করে?

4
আপনি কি নিশ্চিত যে সমস্ত কোড এবং সমস্ত রত্ন থ্রেডসেফ হবে? কি রিলিজ নোট বলে যে পাগল নিজেই threadsafe হবে না যে অন্য সব কিছুর এটা সঙ্গে ব্যবহার করা হয়েছে
enthrops

একাধিক-থ্রেড পরীক্ষাগুলি থ্রেডসেফের ঝুঁকি সবচেয়ে খারাপ হতে পারে। যখন আপনাকে আপনার পরীক্ষার ক্ষেত্রে আশেপাশের পরিবেশের পরিবর্তনশীলটির মান পরিবর্তন করতে হয়, আপনি তাত্ক্ষণিকভাবে থ্রেডসেফ হন না। কিভাবে আপনি এই চারপাশে কাজ করবে? এবং হ্যাঁ, সমস্ত রত্ন থ্রেডসেফ হতে হবে।
Lukas Oberhuber

উত্তর:


110

মূল ডেটা স্ট্রাকচারগুলির কোনওটিই থ্রেড নিরাপদ নয়। রুবীর সাথে যে জাহাজগুলি সম্পর্কে আমি কেবল জানি তা হ'ল স্ট্যান্ডার্ড লাইব্রেরি ( require 'thread'; q = Queue.new) -এর সারি বাস্তবায়ন ।

এমআরআইয়ের জিআইএল থ্রেড সুরক্ষা সমস্যা থেকে আমাদের বাঁচায় না। এটি কেবল নিশ্চিত করে যে দুটি থ্রেড একই সাথে রুবি কোড চালাতে পারে না , অর্থাত্ একই সময়ে দুটি পৃথক সিপিইউতে। আপনার কোডের যে কোনও পয়েন্টে থ্রেডগুলি এখনও বিরতি দেওয়া এবং পুনরায় শুরু করা যেতে পারে। আপনি যদি @n = 0; 3.times { Thread.start { 100.times { @n += 1 } } }একাধিক থ্রেড থেকে ভাগ করা ভেরিয়েবলকে রূপান্তর করার মতো কোড লিখেন তবে ভাগ করে নেওয়া ভেরিয়েবলের মান পরে নির্ধারক নয়। জিআইএল কম-বেশি একটি একক কোর সিস্টেমের অনুকরণ, এটি সঠিক সমবর্তী প্রোগ্রামগুলি লেখার মৌলিক বিষয়গুলিকে পরিবর্তন করে না।

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

রুবিতে, অন্যান্য সাম্প্রতিক রানটাইমের মতো, একের অধিক ক্রিয়াকলাপ থ্রেড নিরাপদ নয়। @n += 1থ্রেড নিরাপদ নয়, কারণ এটি একাধিক অপারেশন। @n = 1থ্রেডটি নিরাপদ কারণ এটি একটি অপারেশন (এটি হুডের অধীনে প্রচুর অপারেশন, এবং আমি সম্ভবত এটি "থ্রেড নিরাপদ" কেন বিশদটি বর্ণনা করার চেষ্টা করেছি তবে শেষ পর্যন্ত আপনি কার্যনির্বাহী থেকে অসামঞ্জস্যপূর্ণ ফলাফল পাবেন না )। @n ||= 1, না এবং অন্য কোনও শর্টহ্যান্ড অপারেশন + অ্যাসাইনমেন্ট হয়। একটি ভুল যা আমি বহুবার করেছি তা হ'ল লেখা return unless @started; @started = true, যা মোটেই থ্রেড নিরাপদ নয়।

আমি রুবির পক্ষে থ্রেড নিরাপদ এবং নন-থ্রেড নিরাপদ বিবৃতিগুলির কোনও অনুমোদনের তালিকা জানি না, তবে থাম্বের একটি সহজ নিয়ম রয়েছে: যদি কোনও অভিব্যক্তি কেবল একটি (পার্শ্ব-প্রভাব মুক্ত) অপারেশন করে তবে এটি সম্ভবত থ্রেড নিরাপদ। উদাহরণস্বরূপ: a + bঠিক আছে, a = bএটিও ঠিক আছে, এবং a.foo(b)ঠিক আছে, যদি পদ্ধতিটি fooপার্শ্ব-প্রতিক্রিয়া মুক্ত থাকে (যেহেতু রুবির কোনও কিছুই একটি মেথড কল, এমনকি অনেক ক্ষেত্রে অ্যাসাইনমেন্ট, এটি অন্যান্য উদাহরণগুলির ক্ষেত্রেও যায়)। এই প্রসঙ্গে পার্শ্ব-প্রতিক্রিয়া মানে এমন জিনিস যা রাষ্ট্র পরিবর্তন করে। def foo(x); @x = x; endহয় না পার্শ্ব প্রতিক্রিয়া বিনামূল্যে।

রুবিতে থ্রেড নিরাপদ কোড লেখার বিষয়ে একটি কঠিন বিষয় হ'ল অ্যারে, হ্যাশ এবং স্ট্রিং সহ সমস্ত মূল ডেটা স্ট্রাকচার পরিবর্তনযোগ্য। দুর্ঘটনাক্রমে আপনার রাজ্যের একটি অংশ ফাঁস করা খুব সহজ, এবং যখন সেই অংশটি পরিবর্তনযোগ্য জিনিসগুলি সত্যিই খারাপ হয়ে যায়। নিম্নলিখিত কোড বিবেচনা করুন:

class Thing
  attr_reader :stuff

  def initialize(initial_stuff)
    @stuff = initial_stuff
    @state_lock = Mutex.new
  end

  def add(item)
    @state_lock.synchronize do
      @stuff << item
    end
  end
end

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

আর একটি ক্লাসিক রুবির উদাহরণ হ'ল:

STANDARD_OPTIONS = {:color => 'red', :count => 10}

def find_stuff
  @some_service.load_things('stuff', STANDARD_OPTIONS)
end

find_stuffএটি ব্যবহৃত প্রথমবারে ঠিকঠাক কাজ করে তবে দ্বিতীয়বারের মতো অন্য কিছু দেয়। কেন? load_thingsপদ্ধতি এটা পাস অপশন হ্যাশ মালিক, এবং আছে মনে ঘটবে color = options.delete(:color)। এখন STANDARD_OPTIONSধ্রুবকের আর মান হয় না। ধ্রুবকরা কেবল তারা যা উল্লেখ করে তাতে স্থির থাকে, তারা যে ডেটা কাঠামোকে উল্লেখ করেছে তার স্থায়িত্বের গ্যারান্টি দেয় না। এই কোডটি একই সাথে চালানো হলে কী হবে তা ভেবে দেখুন।

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

থ্রেডেড পরিবেশে রেলগুলি চালানো এখন বেশ কিছুক্ষণ সম্ভব হয়েছে, সুতরাং রেল বিশেষজ্ঞ না হয়ে আমি এখনও এতদূর যেতে পারি যে এটি যখন নিজেই রেলের কাছে আসে তখন আপনাকে থ্রেড সুরক্ষার জন্য কোনও চিন্তা করতে হবে না। আপনি উপরে উল্লিখিত কিছু কাজ করে এখনও সুরক্ষিত অ্যাপ্লিকেশন তৈরি করতে পারেন যা থ্রেড নিরাপদ নয়। যখন এটি আসে অন্য রত্নগুলি ধরে নেয় যে তারা থ্রেড নিরাপদে নেই যতক্ষণ না তারা বলে যে তারা তারা, এবং যদি তারা বলে যে তারা ধরে নিচ্ছে যে তারা নয় তবে তাদের কোডটি দেখুন (তবে কেবল আপনি দেখতে পাচ্ছেন যে তারা এ জাতীয় জিনিসগুলিতে চলেছে)@n ||= 1 এর অর্থ এই নয় যে তারা থ্রেড নিরাপদ নয়, এটি সঠিক প্রসঙ্গে একটি উপযুক্ত বৈধ জিনিস - আপনার পরিবর্তে বৈশ্বিক পরিবর্তনশীলগুলিতে পরিবর্তনীয় রাষ্ট্রের মতো জিনিসগুলির সন্ধান করা উচিত, এটি কীভাবে পরিবর্তিত পদার্থকে তার পদ্ধতিগুলিতে পরিচালিত করে এবং বিশেষত কীভাবে এটি পরিচালনা করে অপশন হ্যাশগুলি পরিচালনা করে)।

অবশেষে, থ্রেড অনিরাপদ হওয়া একটি ট্রানজিটিভ সম্পত্তি। থ্রেড নিরাপদ নয় এমন কিছু ব্যবহার করে তা নিজেই থ্রেড নিরাপদ নয়।


দুর্দান্ত উত্তর। একটি সাধারণ রেলস অ্যাপটি মাল্টি-প্রক্রিয়া হিসাবে বিবেচনা করে (যেমন আপনি বর্ণনা করেছেন, একই অ্যাপে অ্যাক্সেস করা বিভিন্ন বিবিধ ব্যবহারকারী), আমি অবাক হয়ে যাচ্ছি যে সম্মতিযুক্ত মডেলের থ্রেডগুলির প্রান্তিক ঝুঁকি কী ... অন্য কথায়, আরও কত "বিপজ্জনক" যদি আপনি ইতিমধ্যে প্রক্রিয়াগুলির মাধ্যমে কিছু সামঞ্জস্যের সাথে ডিল করছেন তবে থ্রেডেড মোডে চালাবেন?
আদাচিকিত্বর

4
@ তারা একটি টন ধন্যবাদ। ধ্রুবক স্টাফ একটি বড় বোমা। এটি প্রক্রিয়াটিও নিরাপদ নয়। যদি একটি অনুরোধে ধ্রুবকটি পরিবর্তিত হয়, এটি পরবর্তী অনুরোধগুলি এমনকি একটি থ্রেডে পরিবর্তিত ধ্রুবকটি দেখাবে। রুবি
কনস্ট্যান্টগুলি

5
কি STANDARD_OPTIONS = {...}.freezeঅগভীর পরিব্যক্তি উপর বাড়াতে
glebm

সত্যিই দুর্দান্ত উত্তর
Cheyne

4
"আপনি যদি @n = 0; 3.times { Thread.start { 100.times { @n += 1 } } }[...] এর মতো কোড লিখেন , তবে ভাগ করে নেওয়া ভেরিয়েবলের মান নির্ধারিত নয়" " - আপনি কি জানেন যে এটি রুবির সংস্করণগুলির মধ্যে পৃথক কিনা? উদাহরণস্বরূপ, আপনার কোডটি 1.8 এ চালানো বিভিন্ন মান দেয় @nতবে 1.9 এবং পরে এটি ধারাবাহিকভাবে @n300 এর সমান বলে মনে হয়
ব্যবহারকারী200783

10

থিওর জবাব ছাড়াও, আপনি যদি কনফিগার্ট.থ্রেডস্যাফে স্যুইচ করেন তবে আমি বিশেষত কারাগুলিতে অনুসন্ধানের জন্য কয়েকটি সমস্যা ক্ষেত্র যুক্ত করব!

  • শ্রেণি ভেরিয়েবল :

    @@i_exist_across_threads

  • ENV :

    ENV['DONT_CHANGE_ME']

  • থ্রেডস :

    Thread.start


9

রেল 4 থেকে শুরু করে, সমস্ত কিছুই ডিফল্টরূপে থ্রেডযুক্ত পরিবেশে চলতে হবে

এটি 100% সঠিক নয়। থ্রেড-সেফ রেলগুলি কেবলমাত্র ডিফল্টরূপে। আপনি যদি প্যাসেঞ্জার (সম্প্রদায়) বা ইউনিকর্নের মতো একাধিক প্রক্রিয়া অ্যাপ্লিকেশন সার্ভারে স্থাপন করেন তবে কোনও পার্থক্য হবে না। আপনি যদি পুমা বা যাত্রীবাহী এন্টারপ্রাইজ> ৪.০ এর মতো বহু-থ্রেড পরিবেশে স্থাপন করেন তবে এই পরিবর্তনটি কেবল আপনাকেই উদ্বেগ দিচ্ছে

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

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

এবং আপনি যদি খাঁটি সমবর্তী রুবি প্রোগ্রামিং লিখতে চান, যেখানে আপনার একাধিক থ্রেড দ্বারা অ্যাক্সেস করা এমন কিছু ডেটা স্ট্রাকচারের প্রয়োজন হতে পারে আপনি থ্রেড_সেফ মণিটি দরকারী খুঁজে পেতে পারেন ।

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