เมื่อใดที่คุณจะต้องใช้ "กระทู้นับแสน"


31

Erlang, Go และ Rust อ้างสิทธิ์ทั้งหมดไม่ทางใดก็ทางหนึ่งซึ่งสนับสนุนการเขียนโปรแกรมพร้อมกันด้วย "threads" / coroutines ราคาถูก ไปคำถามที่พบบ่อยฯ :

เป็นจริงในการสร้าง goroutines หลายแสนในพื้นที่ที่อยู่เดียวกัน

The Rust Tutorialพูดว่า:

เนื่องจากงานมีราคาถูกกว่าอย่างมากในการสร้างกว่าเธรดดั้งเดิม Rust สามารถสร้างงานพร้อมกันนับแสนในระบบ 32 บิตโดยทั่วไป

เอกสารของ Erlangกล่าวว่า:

ขนาดฮีพเริ่มต้นเริ่มต้นที่ 233 คำนั้นค่อนข้างอนุรักษ์นิยมเพื่อสนับสนุนระบบ Erlang ที่มีกระบวนการหลายแสนหรือหลายล้านกระบวนการ

คำถามของฉัน: แอปพลิเคชั่นประเภทใดที่ต้องการเธรดการทำงานพร้อมกันจำนวนมาก? มีเพียงเว็บเซิร์ฟเวอร์ที่ยุ่งที่สุดเท่านั้นที่จะได้รับผู้เยี่ยมชมหลายพันคนพร้อมกัน แอปพลิเคชั่นประเภทบอส - คนงาน / การส่งงานที่ฉันเขียน Hit ลดลงส่งคืนเมื่อจำนวนเธรด / กระบวนการมากกว่าจำนวนแกนประมวลผลทางกายภาพมาก ฉันคิดว่ามันสมเหตุสมผลสำหรับแอปพลิเคชั่นตัวเลข แต่ในความเป็นจริงคนส่วนใหญ่มอบหมายให้ขนานกับไลบรารีของบุคคลที่สามที่เขียนใน Fortran / C / C ++ ไม่ใช่ภาษารุ่นใหม่กว่านี้


5
ฉันคิดว่าแหล่งที่มาของความสับสนของคุณคือ: microthreads / task / etc เหล่านี้ไม่ได้มีไว้เพื่อทดแทนเธรด / กระบวนการของระบบปฏิบัติการที่คุณพูดถึงและพวกเขาไม่ได้ตั้งใจจะใช้สำหรับการแบ่งจำนวนชิ้นใหญ่ ๆ ระหว่างสองสามแกน (ตามที่คุณกล่าวไว้อย่างถูกต้องไม่มีจุดใดที่มีเธรด 100k บน 4 คอร์สำหรับจุดประสงค์นั้น)
us2012

1
ถ้าอย่างนั้นพวกเขามีความหมายอะไร? บางทีฉันเป็นคนไร้เดียงสา แต่ฉันไม่เคยพบสถานการณ์ที่แนะนำ coroutines / etc จะทำให้โปรแกรมการดำเนินการแบบเธรดเดียวง่ายขึ้น และฉันก็สามารถบรรลุระดับการเห็นพ้องด้วย "ต่ำ" กับกระบวนการซึ่งบน Linux ฉันสามารถเปิดตัวหลายร้อยหรือหลายพันโดยไม่ต้องเหนื่อย
user39019

มันไม่มีเหตุผลอะไรเลยที่จะมีงานหลายอย่างที่ใช้งานได้จริง ไม่ได้หมายความว่าคุณไม่สามารถมีงานจำนวนมากที่ส่วนใหญ่ถูกบล็อกเพียงแค่รอสิ่งที่จะเกิดขึ้น
Loren Pechtel

5
แนวคิดของการทำงานแบบอะซิงโครนัส vs แบบอะซิงโครนัสตามเธรดคือการบอกว่ารหัสผู้ใช้ควรมีสมาธิกับงานที่ต้องเกิดขึ้นแทนที่จะจัดการคนงานที่ปฏิบัติงานเหล่านั้น คิดว่าด้ายเป็นคนงานที่คุณจ้าง การจ้างคนงานมีราคาแพงและถ้าคุณทำคุณต้องการให้พวกเขาทำงานหนักมากที่สุดเท่าที่จะเป็นไปได้ 100% ระบบจำนวนมากสามารถมีลักษณะเป็นงานที่รอดำเนินการหลายร้อยหรือหลายพัน แต่คุณไม่ต้องการคนงานหลายร้อยหรือหลายพันคน
Eric Lippert

ความคิดเห็นของ @ EricLippert ต่อเนื่องมีหลายสถานการณ์ที่มีงานหลายแสนรายการ ตัวอย่าง # 1: การสลายตัวของงานข้อมูลแบบขนานเช่นการประมวลผลภาพ ตัวอย่าง # 2: เซิร์ฟเวอร์ที่สนับสนุนลูกค้านับแสนรายซึ่งแต่ละแห่งสามารถออกคำสั่งได้ตลอดเวลา แต่ละงานจะต้องมี "บริบทการดำเนินการที่มีน้ำหนักเบา" ของตัวเอง - ความสามารถในการจดจำสถานะที่อยู่ในนั้น (โปรโตคอลการสื่อสาร) และคำสั่งที่กำลังดำเนินการอยู่ในขณะนี้และอย่างอื่น น้ำหนักเบาสามารถทำได้ตราบใดที่แต่ละตัวมี call stack ตื้น ๆ
rwong

คำตอบ:


19

กรณีการใช้งานอย่างหนึ่ง - websockets:
เนื่องจาก websockets มีอายุการใช้งานที่ยาวนานเมื่อเปรียบเทียบกับคำของ่าย ๆ บนเซิร์ฟเวอร์ที่ไม่ว่าง websockets จำนวนมากจะสะสมอยู่ตลอดเวลา microthreads ช่วยให้คุณสร้างแบบจำลองแนวความคิดที่ดีและยังใช้งานง่าย

โดยทั่วไปกรณีที่หน่วยอิสระจำนวนมากกำลังรอเหตุการณ์บางอย่างเกิดขึ้นควรเป็นกรณีการใช้งานที่ดี


15

มันอาจช่วยให้คิดได้ว่า Erlang นั้นถูกออกแบบมาเพื่อทำสิ่งใดซึ่งเป็นการจัดการด้านโทรคมนาคม กิจกรรมเช่นการกำหนดเส้นทางการสลับการรวบรวม / การรวมเซ็นเซอร์ ฯลฯ

นำเรื่องนี้เข้าสู่โลกเว็บ - พิจารณาระบบเช่นทวิตเตอร์ ระบบอาจไม่ใช้ microthreads ในการสร้างหน้าเว็บ แต่สามารถใช้ในการรวบรวม / แคช / กระจายทวีต

บทความนี้อาจช่วยเพิ่มเติม


11

ในภาษาที่คุณไม่ได้รับอนุญาตให้แก้ไขตัวแปรการกระทำง่ายๆในการดูแลรักษาต้องใช้บริบทการดำเนินการแยกต่างหาก (ซึ่งคนส่วนใหญ่จะเรียกเธรดและ Erlang เรียกกระบวนการ) โดยพื้นฐานแล้วทุกอย่างเป็นงาน

พิจารณาฟังก์ชั่น Erlang นี้ซึ่งรักษาเคาน์เตอร์:

counter(Value) ->
    receive                               % Sit idle until a message is received
        increment -> counter(Value + 1);  % Restart with incremented value
        decrement -> counter(Value - 1);  % Restart with decremented value
        speak     ->
            io:fwrite("~B~n", [Value]),
            counter(Value);               % Restart with unaltered value
        _         -> counter(Value)       % Anything else?  Do nothing.
    end.

ในภาษา OO แบบดั้งเดิมเช่น C ++ หรือ Java คุณจะทำสิ่งนี้ได้โดยมีคลาสที่มีสมาชิกคลาสส่วนตัววิธีสาธารณะเพื่อรับหรือเปลี่ยนสถานะและวัตถุอินสแตนซ์สำหรับแต่ละตัวนับ Erlang แทนที่ความคิดของวัตถุ instantiated ด้วยกระบวนการความคิดของวิธีการที่มีข้อความและการบำรุงรักษาของรัฐด้วยการเรียกหางที่รีสตาร์ทฟังก์ชั่นด้วยค่าอะไรก็ตามที่ประกอบขึ้นเป็นสถานะใหม่ ประโยชน์ที่ซ่อนอยู่ในรุ่นนี้ - และส่วนใหญ่ของ Erlang d'être - ของ Erlang คือภาษาจะจัดลำดับการเข้าถึงค่าตัวนับโดยอัตโนมัติผ่านการใช้คิวข้อความทำให้การใช้งานรหัสพร้อมกันนั้นง่ายมากด้วยความปลอดภัยระดับสูง .

คุณอาจเคยชินกับแนวคิดที่ว่า context switch มีราคาแพงซึ่งยังคงเป็นจริงจากมุมมองของโฮสต์ระบบปฏิบัติการ รันไทม์ของ Erlang นั้นเป็นระบบปฏิบัติการขนาดเล็กที่ได้รับการปรับแต่งเพื่อให้การสลับระหว่างกระบวนการของตัวเองนั้นรวดเร็วและมีประสิทธิภาพทั้งหมดในขณะที่การรักษาจำนวนการสลับบริบทระบบปฏิบัติการจะลดลงเหลือน้อยที่สุด ด้วยเหตุนี้การมีกระบวนการหลายพันรายการจึงไม่เป็นปัญหาและได้รับการสนับสนุน


1
แอปพลิเคชันสุดท้ายของคุณcounter/1ควรใช้ตัวพิมพ์เล็ก c;) ฉันพยายามแก้ไข แต่ StackExchange ไม่ชอบการแก้ไข 1 อักขระ
d11wtq

4

คำถามของฉัน: แอปพลิเคชั่นประเภทใดที่ต้องการเธรดการทำงานพร้อมกันจำนวนมาก?

1) ความจริงที่ว่าภาษา "ตาชั่ง" หมายความว่ามีโอกาสน้อยที่คุณจะต้องทิ้งภาษานั้นเมื่อสิ่งต่าง ๆ มีความซับซ้อนมากขึ้นตามถนน (นี่เรียกว่าแนวคิด "ผลิตภัณฑ์ทั้งหมด") หลายคนกำลังทิ้ง Apache สำหรับ Nginx ด้วยเหตุผลนี้ หากคุณอยู่ใกล้กับ "ขีด จำกัด สูงสุด" ที่กำหนดโดยค่าใช้จ่ายของกระทู้คุณจะกลัวและเริ่มคิดหาวิธีที่จะผ่านมันไป เว็บไซต์ไม่สามารถคาดการณ์ปริมาณการรับส่งข้อมูลได้ดังนั้นการใช้เวลาเพียงเล็กน้อยในการทำสิ่งต่าง ๆ ที่ปรับขนาดได้นั้นสมเหตุสมผล

2) หนึ่ง goroutine ต่อคำขอเพียงแค่เริ่มต้น มีเหตุผลมากมายที่จะใช้ goroutines ภายใน

  • พิจารณาเว็บแอปที่มีคำร้องขอพร้อมกัน 100 คำร้องขอแต่ละคำขอสร้างคำขอแบ็คเอนด์ครบ 100 รายการ ตัวอย่างที่ชัดเจนคือเครื่องมือรวบรวมเครื่องมือค้นหา แต่แอพพลิเคชั่นใด ๆ ก็สามารถสร้างโกโรไทน์สำหรับ "พื้นที่" แต่ละรายการบนหน้าจอจากนั้นสร้างแอปเหล่านั้นแยกต่างหากแทน ตัวอย่างเช่นทุกหน้าใน Amazon.com ประกอบด้วยคำขอแบ็กเอนด์มากกว่า 150+ ชุดประกอบขึ้นเพื่อคุณโดยเฉพาะ คุณไม่สังเกตเห็นเพราะมันอยู่ในแนวขนานไม่ใช่ต่อเนื่องและ "พื้นที่" แต่ละอันคือบริการเว็บของตัวเอง
  • พิจารณาแอพที่มีความน่าเชื่อถือและความหน่วงสูง คุณอาจต้องการแต่ละคำขอเข้ามาเพื่อปิดไฟคำขอ back-end น้อยและผลตอบแทนแล้วแต่จำนวนใดข้อมูลกลับมาเป็นอันดับแรก
  • พิจารณา "ลูกค้าเข้าร่วม" ที่ทำในแอปของคุณ แทนที่จะพูดว่า "สำหรับแต่ละองค์ประกอบรับข้อมูล" คุณสามารถแยก goroutines ออกเป็นกลุ่มได้ หากคุณมีฐานข้อมูลทาสจำนวนมากที่จะสืบค้นคุณจะได้เวลา N เร็วขึ้นอย่างน่าอัศจรรย์ ถ้าคุณทำไม่ได้มันจะไม่ช้าลง

hit diminishing จะส่งกลับเมื่อจำนวนเธรด / กระบวนการมากกว่าจำนวนแกนประมวลผล

ผลการดำเนินงานไม่ได้เป็นเหตุผลเดียวที่จะเลิกโปรแกรมลงCSP จริง ๆ แล้วมันสามารถทำให้โปรแกรมเข้าใจง่ายขึ้นและปัญหาบางอย่างสามารถแก้ไขได้ด้วยรหัสน้อยกว่ามาก

เช่นเดียวกับสไลด์ที่ลิงก์ด้านบนการมีการทำงานพร้อมกันในรหัสของคุณเป็นวิธีการจัดการปัญหา การไม่มีโกโรไทน์เหมือนไม่มีโครงสร้างข้อมูล Map / Dictonary / Hash ในภาษาของคุณ คุณสามารถรับโดยไม่ได้ แต่เมื่อคุณมีแล้วคุณจะเริ่มใช้มันได้ทุกที่และทำให้โปรแกรมของคุณง่ายขึ้น

ในอดีตสิ่งนี้หมายถึง "การเขียนโปรแกรมแบบมัลติเธรดของคุณเอง" แต่มันซับซ้อนและอันตราย - ยังไม่มีเครื่องมือมากมายที่จะทำให้แน่ใจว่าคุณไม่ได้สร้างเผ่าพันธุ์ และคุณจะป้องกันผู้ดูแลในอนาคตจากการทำผิดพลาดได้อย่างไร? หากคุณดูโปรแกรมขนาดใหญ่ / ซับซ้อนคุณจะเห็นว่าพวกเขาใช้ทรัพยากรจำนวนมากไปในทิศทางนั้น

เนื่องจากการทำงานพร้อมกันไม่ใช่ส่วนหนึ่งของภาษาส่วนใหญ่โปรแกรมเมอร์ในปัจจุบันจึงมีจุดบอดว่าทำไมจึงมีประโยชน์กับพวกเขา สิ่งนี้จะเห็นได้ชัดมากขึ้นเมื่อโทรศัพท์และนาฬิกาข้อมือหันไปที่ 1,000 คอร์ ใช้งานเครื่องมือตรวจจับการแข่งขันในตัว


2

สำหรับ Erlang เป็นเรื่องปกติที่จะมีหนึ่งกระบวนการต่อการเชื่อมต่อหรืองานอื่น ๆ ตัวอย่างเช่นเซิร์ฟเวอร์เสียงแบบสตรีมอาจมี 1 กระบวนการต่อผู้ใช้ที่เชื่อมต่อ

Erlang VM ได้รับการปรับให้เหมาะสมในการจัดการกระบวนการหลายพันหรือหลายแสนกระบวนการด้วยการทำให้คอนเท็กซ์สวิตช์ราคาถูกมาก


1

ความสะดวกสบาย ย้อนกลับไปเมื่อฉันเริ่มทำการเขียนโปรแกรมแบบมัลติเธรดฉันทำการจำลองและพัฒนาเกมมากมายเพื่อความสนุกสนาน ฉันพบว่ามันเป็นความสะดวกสบายที่ดีเพียงแค่แยกเธรดสำหรับทุกวัตถุและปล่อยให้มันทำเองแทนที่จะประมวลผลแต่ละวง หากรหัสของคุณไม่ถูกรบกวนจากพฤติกรรมที่ไม่ได้กำหนดไว้และคุณไม่มีการชนรหัสก็สามารถทำให้การเข้ารหัสง่ายขึ้น ด้วยพลังที่มีให้เราตอนนี้ถ้าฉันได้กลับเข้าไปในนั้นฉันสามารถจินตนาการได้อย่างง่ายดายปั่นด้ายสองพันกระทู้เนื่องจากมีพลังในการประมวลผลและหน่วยความจำเพียงพอที่จะจัดการกับวัตถุที่ไม่ต่อเนื่องจำนวนมาก!


1

ตัวอย่างง่ายๆสำหรับ Erlang ซึ่งออกแบบมาเพื่อการสื่อสาร: การถ่ายโอนแพ็คเก็ตเครือข่าย เมื่อคุณทำการร้องขอ HTTP หนึ่งรายการคุณอาจมีแพ็คเก็ต TCP / IP นับพัน เพิ่มสิ่งนี้ที่ทุกคนเชื่อมต่อในเวลาเดียวกันและคุณมีกรณีการใช้งานของคุณ

พิจารณาแอพพลิเคชั่นมากมายที่ บริษัท ใหญ่ ๆ ใช้ภายในเพื่อจัดการคำสั่งซื้อหรือสิ่งที่พวกเขาต้องการ เว็บเซิร์ฟเวอร์ไม่ใช่สิ่งเดียวที่ต้องการเธรด


-2

งานการเรนเดอร์บางอย่างต้องคำนึงถึงที่นี่ หากคุณทำ ops เป็นจำนวนมากในทุก ๆ พิกเซลของภาพและถ้า ops เหล่านั้นเป็นแบบขนานกันได้แม้กระทั่งรูปภาพ 1024x768 ที่ค่อนข้างเล็กจะอยู่ในวงเล็บ "หลายแสน"


2
เมื่อหลายปีก่อนฉันใช้เวลาหลายปีในการประมวลผลภาพ FLIR ตามเวลาจริงโดยบีบอัดภาพ 256x256 ที่ 30 เฟรมต่อวินาที เว้นแต่คุณจะมีตัวประมวลผลฮาร์ดแวร์จำนวนมากและวิธีการแบ่งข้อมูลอย่างไม่มีตะเข็บระหว่างกันสิ่งสุดท้ายที่คุณต้องการทำคือเพิ่มการสลับบริบทการแข่งขันในหน่วยความจำและการแคชแคชกับค่าใช้จ่ายในการคำนวณจริง
John R. Strohm

ขึ้นอยู่กับงานที่ทำ หากสิ่งที่คุณทำคือการมอบงานให้กับฮาร์ดแวร์หลัก / หน่วยดำเนินการหลังจากนั้นคุณสามารถลืมมันได้อย่างมีประสิทธิภาพ (และทราบว่านี่เป็นวิธีการทำงานของ GPU ดังนั้นนี่ไม่ใช่สถานการณ์สมมุติ) วิธีการคือ ถูกต้อง.
Maximus Minimus
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.