share a struct with lifetime parameter which implements `dyn` trait across threads

huangapple go评论175阅读模式
英文:

share a struct with lifetime parameter which implements `dyn` trait across threads

问题

如何安全地在线程之间共享实现了具有生命周期参数的dyn trait的结构体。这是我的代码:

main.rs

  1. use std::sync::atomic::{AtomicBool, Ordering};
  2. use std::sync::{Arc, mpsc, Mutex};
  3. use std::thread;
  4. trait Draw {
  5. fn draw(&self);
  6. }
  7. #[derive(Default)]
  8. struct Button {
  9. }
  10. impl Draw for Button {
  11. fn draw(&self) {
  12. println!("draw button");
  13. }
  14. }
  15. #[derive(Default)]
  16. struct SelectionBox {
  17. }
  18. impl Draw for SelectionBox {
  19. fn draw(&self) {
  20. println!("draw selection box");
  21. }
  22. }
  23. #[derive(Default)]
  24. struct TextField {
  25. }
  26. impl Draw for TextField {
  27. fn draw(&self) {
  28. println!("draw text field");
  29. }
  30. }
  31. pub struct RunningThreadInterface<'a, T> {
  32. pub instance: Arc<T>,
  33. pub thread_join_handle: thread::JoinHandle<()>,
  34. }
  35. pub trait StartThread<'a, T> {
  36. fn start(self, thread_id: String) -> RunningThreadInterface<'a, T>;
  37. fn run(&self);
  38. }
  39. pub trait TerminateThread {
  40. fn stop(&mut self);
  41. fn wait(self);
  42. }
  43. struct Screen<'a> {
  44. widgets: Mutex<Vec<&'a (dyn Draw + Send + Sync)>>,
  45. rx: Mutex<mpsc::Receiver<String>>,
  46. terminate_flag: AtomicBool,
  47. }
  48. impl<'a> Screen<'a> {
  49. fn new(rx: mpsc::Receiver<String>) -> Screen<'a> {
  50. Screen {
  51. widgets: Mutex::new(Vec::new()),
  52. rx: Mutex::new(rx),
  53. terminate_flag: AtomicBool::new(false),
  54. }
  55. }
  56. fn add(&mut self, widget: &'a (dyn Draw + Send + Sync)) {
  57. self.widgets.lock().unwrap().push(widget);
  58. }
  59. fn draw_widgets(&self) {
  60. for widget in &*self.widgets.lock().unwrap() {
  61. widget.draw();
  62. }
  63. }
  64. }
  65. impl<'a> StartThread<'a, Screen<'a>> for Screen<'a> {
  66. fn start(self, thread_id: String) -> RunningThreadInterface<'a, Screen<'a>> {
  67. let screen = Arc::new(self);
  68. RunningThreadInterface {
  69. instance: Arc::clone(&screen),
  70. thread_join_handle: thread::Builder::new().name(thread_id).spawn(move || screen.run()).ok().unwrap(),
  71. }
  72. }
  73. fn run(&self) {
  74. while !self.terminate_flag.load(Ordering::SeqCst) {
  75. self.rx.lock().unwrap().recv().unwrap();
  76. }
  77. }
  78. }
  79. impl<'a> TerminateThread for RunningThreadInterface<'a, Screen<'a>> {
  80. fn stop(&mut self) {
  81. self.instance.terminate_flag.store(true, Ordering::SeqCst);
  82. }
  83. fn wait(self) {
  84. self.thread_join_handle.join();
  85. }
  86. }
  87. fn main() {
  88. let button: Button = Default::default();
  89. let selection_box: SelectionBox = Default::default();
  90. let text_field: TextField = Default::default();
  91. let (_tx, rx) = mpsc::channel();
  92. let mut screen = Screen::new(rx);
  93. screen.add(&button);
  94. screen.add(&selection_box);
  95. screen.add(&text_field);
  96. screen.draw_widgets();
  97. println!("");
  98. button.draw();
  99. selection_box.draw();
  100. text_field.draw();
  101. }

错误:

  1. error[E0521]: borrowed data escapes outside of method
  2. --> src/main.rs:90:33
  3. |
  4. 85 | impl<'a> StartThread<'a, Screen<'a>> for Screen<'a> {
  5. | -- lifetime `'a` defined here
  6. 86 | fn start(self, thread_id: String) -> RunningThreadInterface<'a, Screen<'a>> {
  7. | ---- `self` is a reference that is only valid in the method body
  8. ...
  9. 90 | thread_join_handle: thread::Builder::new().name(thread_id).spawn(move || screen.run()).ok().unwrap(),
  10. | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  11. | |
  12. | `self` escapes the method body here
  13. | argument requires that `'a` must outlive `'static`
  14. |
  15. = note: requirement occurs because of the type `Screen<'_>`
  16. = note: the struct `Screen<'a>` is invariant over the parameter `'a`
  17. = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance

请注意,我不能更改现有的StartThreadTerminateThread trait的语法,但如果现有设计不好(可以改进),我愿意听取建议。

英文:

How to share a struct with lifetime parameter which implements dyn trait across threads safely. This is my code

main.rs

  1. use std::sync::atomic::{AtomicBool, Ordering};
  2. use std::sync::{Arc, mpsc, Mutex};
  3. use std::thread;
  4. trait Draw {
  5. fn draw(&amp;self);
  6. }
  7. #[derive(Default)]
  8. struct Button {
  9. }
  10. impl Draw for Button {
  11. fn draw(&amp;self) {
  12. println!(&quot;draw button&quot;);
  13. }
  14. }
  15. #[derive(Default)]
  16. struct SelectionBox {
  17. }
  18. impl Draw for SelectionBox {
  19. fn draw(&amp;self) {
  20. println!(&quot;draw selection box&quot;);
  21. }
  22. }
  23. #[derive(Default)]
  24. struct TextField {
  25. }
  26. impl Draw for TextField {
  27. fn draw(&amp;self) {
  28. println!(&quot;draw text field&quot;);
  29. }
  30. }
  31. pub struct RunningThreadInterface&lt;T&gt; {
  32. pub instance: Arc&lt;T&gt;,
  33. pub thread_join_handle: thread::JoinHandle&lt;()&gt;,
  34. }
  35. pub trait StartThread&lt;T&gt; {
  36. fn start(self, thread_id: String) -&gt; RunningThreadInterface&lt;T&gt;;
  37. fn run(&amp;self);
  38. }
  39. pub trait TerminateThread {
  40. fn stop(&amp;mut self);
  41. fn wait(self);
  42. }
  43. struct Screen&lt;&#39;a&gt; {
  44. widgets: Mutex&lt;Vec&lt;&amp;&#39;a (dyn Draw + Send + Sync)&gt;&gt;,
  45. rx: Mutex&lt;mpsc::Receiver&lt;String&gt;&gt;,
  46. terminate_flag: AtomicBool,
  47. }
  48. impl&lt;&#39;a&gt; Screen&lt;&#39;a&gt; {
  49. fn new(rx: mpsc::Receiver&lt;String&gt;) -&gt; Screen&lt;&#39;a&gt; {
  50. Screen {
  51. widgets: Mutex::new(Vec::new()),
  52. rx: Mutex::new(rx),
  53. terminate_flag: AtomicBool::new(false),
  54. }
  55. }
  56. fn add(&amp;mut self, widget: &amp;&#39;a (dyn Draw + Send + Sync)) {
  57. self.widgets.lock().unwrap().push(widget);
  58. }
  59. fn draw_widgets(&amp;self) {
  60. for widget in &amp;*self.widgets.lock().unwrap() {
  61. widget.draw();
  62. }
  63. }
  64. }
  65. impl&lt;&#39;a&gt; StartThread&lt;Screen&lt;&#39;a&gt;&gt; for Screen&lt;&#39;a&gt; {
  66. fn start(self, thread_id: String) -&gt; RunningThreadInterface&lt;Screen&lt;&#39;a&gt;&gt; {
  67. let screen = Arc::new(self);
  68. RunningThreadInterface {
  69. instance: Arc::clone(&amp;screen),
  70. thread_join_handle: thread::Builder::new().name(thread_id).spawn(move || screen.run()).ok().unwrap(),
  71. }
  72. }
  73. fn run(&amp;self) {
  74. while !self.terminate_flag.load(Ordering::SeqCst) {
  75. self.rx.lock().unwrap().recv().unwrap();
  76. }
  77. }
  78. }
  79. impl&lt;&#39;a&gt; TerminateThread for RunningThreadInterface&lt;Screen&lt;&#39;a&gt;&gt; {
  80. fn stop(&amp;mut self) {
  81. self.instance.terminate_flag.store(true, Ordering::SeqCst);
  82. }
  83. fn wait(self) {
  84. self.thread_join_handle.join();
  85. }
  86. }
  87. fn main() {
  88. let button: Button = Default::default();
  89. let selection_box: SelectionBox = Default::default();
  90. let text_field: TextField = Default::default();
  91. let (_tx, rx) = mpsc::channel();
  92. let mut screen = Screen::new(rx);
  93. screen.add(&amp;button);
  94. screen.add(&amp;selection_box);
  95. screen.add(&amp;text_field);
  96. screen.draw_widgets();
  97. println!(&quot;&quot;);
  98. button.draw();
  99. selection_box.draw();
  100. text_field.draw();
  101. }

Error

  1. error[E0521]: borrowed data escapes outside of method
  2. --&gt; src/main.rs:90:33
  3. |
  4. 85 | impl&lt;&#39;a&gt; StartThread&lt;Screen&lt;&#39;a&gt;&gt; for Screen&lt;&#39;a&gt; {
  5. | -- lifetime `&#39;a` defined here
  6. 86 | fn start(self, thread_id: String) -&gt; RunningThreadInterface&lt;Screen&lt;&#39;a&gt;&gt; {
  7. | ---- `self` is a reference that is only valid in the method body
  8. ...
  9. 90 | thread_join_handle: thread::Builder::new().name(thread_id).spawn(move || screen.run()).ok().unwrap(),
  10. | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  11. | |
  12. | `self` escapes the method body here
  13. | argument requires that `&#39;a` must outlive `&#39;static`
  14. |
  15. = note: requirement occurs because of the type `Screen&lt;&#39;_&gt;`, which makes the generic argument `&#39;_` invariant
  16. = note: the struct `Screen&lt;&#39;a&gt;` is invariant over the parameter `&#39;a`
  17. = help: see &lt;https://doc.rust-lang.org/nomicon/subtyping.html&gt; for more information about variance
  18. For more information about this error, try `rustc --explain E0521`.

Please note I cannot change the existing syntax of StartThread and TerminateThread traits, but I'm open for suggestions if the exiting design isn't good(can be made better)

答案1

得分: 1

I don't know if this generic enough for you, but if you restrict the generic lifetime to be static (impl<'a: 'static> StartThread<Screen<'a>> for Screen<'a>) then your example runs.

You can also do it in this equivalent way:

  1. impl StartThread<Screen<'static>> for Screen<'static> {
  2. fn start(self, thread_id: String) -> RunningThreadInterface<Screen<'static>> {
  3. let screen = Arc::new(self);
  4. RunningThreadInterface {
  5. instance: Arc::clone(&screen),
  6. thread_join_handle: thread::Builder::new().name(thread_id).spawn(move || screen.run()).ok().unwrap(),
  7. }
  8. }
  9. fn run(&self) {
  10. while !self.terminate_flag.load(Ordering::SeqCst) {
  11. self.rx.lock().unwrap().recv().unwrap();
  12. }
  13. }
  14. }
英文:

I don't know if this generic enough for you, but if you restrict the generic lifetime to be static (impl&lt;&#39;a: &#39;static&gt; StartThread&lt;Screen&lt;&#39;a&gt;&gt; for Screen&lt;&#39;a&gt;) then your example runs.

You can also do it in this equivalent way:

  1. impl StartThread&lt;Screen&lt;&#39;static&gt;&gt; for Screen&lt;&#39;static&gt; {
  2. fn start(self, thread_id: String) -&gt; RunningThreadInterface&lt;Screen&lt;&#39;static&gt;&gt; {
  3. let screen = Arc::new(self);
  4. RunningThreadInterface {
  5. instance: Arc::clone(&amp;screen),
  6. thread_join_handle: thread::Builder::new().name(thread_id).spawn(move || screen.run()).ok().unwrap(),
  7. }
  8. }
  9. fn run(&amp;self) {
  10. while !self.terminate_flag.load(Ordering::SeqCst) {
  11. self.rx.lock().unwrap().recv().unwrap();
  12. }
  13. }
  14. }

huangapple
  • 本文由 发表于 2023年5月26日 15:08:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/76338395.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定