วิธีแก้ปัญหาการเข้าใช้ใหม่แบบ Async C # 5


16

ดังนั้นมีบางอย่างเกี่ยวกับฉันที่สนับสนุน async ใหม่ใน C # 5:

ผู้ใช้กดปุ่มที่เริ่มการทำงานของ async การโทรจะส่งกลับทันทีและปั๊มข้อความเริ่มทำงานอีกครั้งนั่นคือจุดรวม

ดังนั้นผู้ใช้สามารถกดปุ่มอีกครั้ง - ก่อให้เกิดความเชื่อมั่นอีกครั้ง เกิดอะไรขึ้นถ้านี่เป็นปัญหา

ในตัวอย่างที่ฉันได้เห็นพวกเขาจะปิดใช้งานปุ่มก่อนการawaitโทรและเปิดใช้งานอีกครั้งหลังจากนั้น ดูเหมือนว่าฉันจะเป็นทางออกที่เปราะบางมากในแอปที่ใช้งานจริง

เราควรเขียนรหัสสถานะเครื่องจักรบางอย่างที่ระบุว่าต้องปิดการใช้งานการควบคุมใดสำหรับชุดการทำงานที่กำหนดไว้ หรือมีวิธีที่ดีกว่า

ฉันอยากที่จะแสดงกล่องโต้ตอบเป็นกิริยาช่วยในช่วงระยะเวลาของการดำเนินการ แต่นี่รู้สึกเหมือนใช้ค้อนขนาดใหญ่

มีใครมีความคิดที่สดใสบ้างไหม


แก้ไข:

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

จะเกิดอะไรขึ้นถ้ามีชุดควบคุมที่ควรปิดใช้งานสำหรับการดำเนินการเฉพาะอย่าง และจะเกิดอะไรขึ้นถ้าการทำงานหลายอย่างทำงานพร้อมกัน?


อะไรที่เปราะบางเกี่ยวกับมัน? มันทำให้ผู้ใช้มีข้อบ่งชี้บางอย่างที่กำลังถูกประมวลผล กำลังดำเนินการเพิ่มตัวบ่งชี้ไดนามิกบางตัวและดูเหมือนว่า UI ที่ดีที่สุดสำหรับฉัน
Steven Jeuris

PS:. C # 4.5 ของ. NET เรียกว่า C # 5 หรือไม่
Steven Jeuris

1
แค่อยากรู้ว่าทำไมคำถามนี้อยู่ที่นี่และไม่ใช่เหรอ? นี่เป็นเรื่องเกี่ยวกับรหัสดังนั้นฉันคิดว่ามันคงอยู่ที่นั่น ...
C. Ross

@ C.Ross นี่เป็นคำถามออกแบบ / UI มากกว่า ไม่มีประเด็นทางเทคนิคที่ต้องอธิบายที่นี่
Morgan Herlocker

เรามีปัญหาที่คล้ายกันในรูปแบบ ajax HTML ที่ผู้ใช้กดปุ่มส่งคำขอ ajax ถูกส่งและเราต้องการปิดกั้นผู้ใช้จากการกดปุ่มส่งอีกครั้งการปิดใช้งานปุ่มเป็นวิธีการแก้ปัญหาที่พบบ่อยมาก
Raynos

คำตอบ:


14

ดังนั้นมีบางอย่างที่ชักชวนฉันเกี่ยวกับการสนับสนุน async ใหม่ใน C # 5: ผู้ใช้กดปุ่มที่เริ่มการทำงานของ async การโทรจะส่งกลับทันทีและปั๊มข้อความเริ่มทำงานอีกครั้งนั่นคือจุดรวม ดังนั้นผู้ใช้สามารถกดปุ่มอีกครั้ง - ก่อให้เกิดความเชื่อมั่นอีกครั้ง เกิดอะไรขึ้นถ้านี่เป็นปัญหา

เริ่มต้นด้วยการสังเกตว่านี่เป็นปัญหาอยู่แล้วแม้ว่าจะไม่รองรับ async ในภาษาก็ตาม หลายสิ่งหลายอย่างอาจทำให้ข้อความถูกตัดออกในขณะที่คุณจัดการกิจกรรม คุณแล้วต้องรหัส defensively สำหรับสถานการณ์นี้; คุณลักษณะภาษาใหม่ทำให้ชัดเจนยิ่งขึ้นซึ่งก็คือความดี

แหล่งที่พบมากที่สุดของข้อความห่วงการทำงานในช่วงเหตุการณ์ที่ก่อให้เกิดความไม่พึงประสงค์อีกครั้ง entrancy DoEventsคือ สิ่งที่ดีเกี่ยวกับawaitเมื่อเทียบกับDoEventsที่awaitทำให้มีโอกาสมากขึ้นที่งานใหม่จะไม่ "หิวโหย" งานที่กำลังทำงานอยู่ DoEventsปั๊มวนรอบข้อความแล้วเรียกใช้ตัวจัดการเหตุการณ์ที่เข้ามาใหม่พร้อมกันในขณะที่awaitมักจะจัดคิวงานใหม่เพื่อที่จะทำงานในบางจุดในอนาคต

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

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

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

ฉันอยากที่จะแสดงกล่องโต้ตอบเป็นกิริยาช่วยในช่วงระยะเวลาของการดำเนินการ แต่นี่รู้สึกเหมือนใช้ค้อนขนาดใหญ่

ผู้ใช้จะเกลียดสิ่งนั้น


ขอบคุณ Eric ฉันคิดว่านี่เป็นคำตอบสุดท้าย - ขึ้นอยู่กับผู้พัฒนาแอปพลิเคชัน
Nicholas Butler

6

ทำไมคุณถึงคิดว่าการปิดใช้งานปุ่มก่อนawaitแล้วจึงเปิดใช้งานอีกครั้งเมื่อการโทรเสร็จสมบูรณ์จึงเปราะบาง เป็นเพราะคุณกังวลว่าปุ่มจะไม่เปิดใช้งานอีกครั้งหรือไม่

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

ครั้งเดียวที่อาจเกิดความยุ่งเหยิงคือถ้ามีการดำเนินการหลายอย่างที่ส่งผลต่อสถานะของปุ่ม แต่ฉันคิดว่าสถานการณ์เหล่านั้นน่าจะหายากมาก


แน่นอนคุณต้องจัดการกับข้อผิดพลาด - ฉันได้อัปเดตคำถามของฉันแล้ว
Nicholas Butler

5

ฉันไม่แน่ใจว่าสิ่งนี้ใช้ได้กับคุณหรือไม่เพราะฉันมักจะใช้ WPF แต่ฉันเห็นคุณอ้างอิงถึงViewModelดังนั้นอาจเป็นไปได้

แทนการปิดการใช้งานปุ่มตั้งธงIsLoading trueจากนั้นองค์ประกอบ UI ใด ๆ ที่คุณต้องการปิดการใช้งานสามารถเชื่อมโยงกับการตั้งค่าสถานะ ผลลัพธ์ที่ได้คือมันกลายเป็นหน้าที่ของ UI ในการแยกแยะสถานะการเปิดใช้งานของตัวเองไม่ใช่ Business Logic ของคุณ

นอกจากนั้นปุ่มส่วนใหญ่ใน WPF ถูกผูกไว้กับICommandและฉันมักจะตั้งค่าICommand.CanExecuteเท่ากับ!IsLoadingดังนั้นมันจะป้องกันคำสั่งจากการดำเนินการโดยอัตโนมัติเมื่อ IsLoading เท่ากับจริง (ปิดการใช้งานปุ่มสำหรับฉันด้วย)


3

ในตัวอย่างที่ฉันเห็นพวกเขาปิดใช้งานปุ่มก่อนที่จะรอสายและเปิดใช้งานอีกครั้งหลังจากนั้น ดูเหมือนว่าฉันจะเป็นทางออกที่เปราะบางมากในแอปที่ใช้งานจริง

ไม่มีอะไรที่เปราะบางเกี่ยวกับเรื่องนี้และเป็นสิ่งที่ผู้ใช้คาดหวัง หากผู้ใช้ต้องรอก่อนกดปุ่มอีกครั้งให้ทำการรอ

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


ขอบคุณ - แต่จะจัดการความซับซ้อนอย่างไร?
นิโคลัสบัตเลอร์

ทางเลือกหนึ่งคือการใส่ส่วนควบคุมที่เกี่ยวข้องลงในคอนเทนเนอร์ ปิดใช้งาน / เปิดใช้งานโดยคอนเทนเนอร์ควบคุมไม่ใช่โดยควบคุม เช่น: EditorControls.Foreach (c => c.Enabled = false);
Morgan Herlocker

2

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

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

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

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


ขอบคุณ - ฉันชอบความคิดนี้ ดังนั้นคุณจะมีวัตถุผู้จัดการสำหรับหน้าต่างของคุณที่นับจำนวนการปิดการใช้งานและเปิดใช้งานการร้องขอสำหรับการควบคุมแต่ละรายการ
Nicholas Butler

@NickButler: คุณมีวัตถุผู้จัดการสำหรับแต่ละหน้าต่างอยู่แล้ว - แบบฟอร์ม ดังนั้นฉันจะมีชั้นเรียนดำเนินการตามคำขอและการนับและเพียงเพิ่มอินสแตนซ์ไปยังแบบฟอร์มที่เกี่ยวข้อง
Jan Hudec

1

แม้ว่า Jan Hudec และ Rachel ได้แสดงความคิดเห็นที่เกี่ยวข้องกันฉันขอแนะนำให้ใช้บางสิ่งบางอย่างเช่นสถาปัตยกรรมModel-View - แสดงสถานะของแบบฟอร์มในวัตถุและผูกองค์ประกอบของแบบฟอร์มกับสถานะนั้น สิ่งนี้จะปิดใช้งานปุ่มในวิธีที่สามารถปรับขนาดสำหรับสถานการณ์ที่ซับซ้อนมากขึ้น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.