none
WPF异步编程,线程协作混乱 RRS feed

  • 问题

  • Hi 大家好,我有一个线程协作问题,具体问题是当用户点击按钮后,程序应后台运行代码执行命令,后台程序运行事件过长,需要异步处理;现在的问题是,当后台程序正在执行之前的任务时,用户发起了第二个请求,当第二个请求发出时,我目前的代码又会创建另外一个线程来处理该任务,但是这么做导致的BUG是任务返回的值是混合的,即第一个任务的返回值混合着第二个任务的返回值。 我在想有没有解决的办法类似于,当后台正在有命令执行时,新加入的任务等待之前的任务完成后再执行。我把问题代码简化一下PO上来,希望大神可以帮我解答一下,最好提供一下代码,多线程这块我刚刚接触,不是很会。

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Windows;
    
    namespace WpfApp7
    {
        /// <summary>
        /// Interaction logic for MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }
    
            private async void Btn1_Click(object sender, RoutedEventArgs e)   //问题出现在这,当这个异步任务执行时,再发起一个这个任务,程序不会等待老任务结束而立刻发起新的任务
            {
                await Task.Run(() =>
                {
                    for (int i = 0; i < 3; i++)
                    {
                        Thread.Sleep(1000);
                        Console.WriteLine(i);
                    }
                });
            }
        }
    }
    

    XAML:

    <Window x:Class="WpfApp7.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:WpfApp7"
            mc:Ignorable="d"
            Title="MainWindow" Height="450" Width="800">
        <Grid>
            <Button Height="70" Width="140" Name="Btn1" Click="Btn1_Click">Click Me</Button>
        </Grid>
    </Window>
    

    2019年1月3日 21:15

答案

  • Hi   CZAupup,  

    >> 我在想有没有解决的办法类似于,当后台正在有命令执行时,新加入的任务等待之前的任务完成后再执行。

    线程的并行执行并不能控制它执行的顺序,你可以通过资源锁来保证执行的准确性。可以通过await等待返回结果. 你目前的方式是想多个任务并行的执行的场景,同时任务之间又需要先后依赖的关系。针对这样的处理逻辑, 你可以使用Task ContinueWith 来保证任务的执行顺序。

    建议:再系统中,需要避免你类似的无限点击创建多任务情况,需要加一些控制来保证程序的稳定。

    下面例子仅供参考。

            Task temp;
            private async void Btn1_Click(object sender, RoutedEventArgs e)   //问题出现在这,当这个异步任务执行时,再发起一个这个任务,程序不会等待老任务结束而立刻发起新的任务
            {
                // task Task
                if (temp == null)
                {
                    temp = Task.Factory.StartNew(() =>
                    {
                        for (int i = 0; i < 3; i++)
                        {
                            Thread.Sleep(1000);
                            Console.WriteLine(" I : " + i.ToString() + " ,ThreadId: " + Thread.CurrentThread.ManagedThreadId.ToString());
                        }
                    });
                }
                else
                {
                    var tasks = temp.ContinueWith((ant) =>
                     {
                         for (int i = 0; i < 3; i++)
                         {
                             Thread.Sleep(1000);
                             Console.WriteLine("I : " + i.ToString() + " ,ThreadId: " + Thread.CurrentThread.ManagedThreadId.ToString());
                         }
                     });
                    temp = tasks;
                }
    
    
       }
    


    你也可以尝试使用使用了ThreadPool,ManualResetEvent: 参考:C#多线程顺序依赖执行控制 


    Task Class 


    Best Regards,

    Yong Lu


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    • 已标记为答案 CZAupup 2019年1月4日 17:03
    2019年1月4日 3:19
    版主

全部回复

  • 可以尝试互斥锁或者类似同步线程技术

    参考

    线程处理


    我不太清楚你代码的具体问题 我按照我的经验写了一个小小的例子

     public class Test : INotifyPropertyChanged
        {
            private int _plus;
    
            public int Plus { get => _plus; set { _plus = value;  OnChanged(new PropertyChangedEventArgs ("Plus")); } }
    
            public event PropertyChangedEventHandler PropertyChanged;
    
            protected void OnChanged(PropertyChangedEventArgs args) => PropertyChanged?.Invoke(this, args);
    
        }
    
        /// <summary>
        /// MainWindow.xaml 的交互逻辑
        /// </summary>
        public partial class MainWindow : Window
        {
            Test test;
    
            public MainWindow()
            {
                InitializeComponent();
                test = new Test();
                Tb.DataContext = test;
                test .Plus= 1;
            }
            
            private async void Button_Click(object sender, RoutedEventArgs e)
            {
                await Task.Run(()=> 
                {
                  var t=  new Thd(Plus);
                    t.Start();  
                    var t2 = new Thd(Sub);
                    t2.Start();
                });
            }
           //减法
            private void Sub()
            {
                for (var i = 0; i < 100000; i++)
                    test.Plus -=1;
            }
            //加法
            private void Plus()
            {
                for (var i = 0; i < 100000; i++)
                    test.Plus+=1;
            }
        }


    针对这个问题 最简单的是使用Jion方法

      private async void Button_Click(object sender, RoutedEventArgs e)
            {
                await Task.Run(()=> 
                {
                  var t=  new Thd(Plus);
                    t.Start();
                   //等待当前线程完成操作
                    t.Join();
                    var t2 = new Thd(Sub);
                    t2.Start();
                });
            }




    • 已编辑 ARM830 2019年1月4日 3:01
    2019年1月4日 0:38
  • 这问题看官方文档啊,微软已经为你准备好了解决办法,,,,,

    please verify my account

    2019年1月4日 3:16
  • Hi   CZAupup,  

    >> 我在想有没有解决的办法类似于,当后台正在有命令执行时,新加入的任务等待之前的任务完成后再执行。

    线程的并行执行并不能控制它执行的顺序,你可以通过资源锁来保证执行的准确性。可以通过await等待返回结果. 你目前的方式是想多个任务并行的执行的场景,同时任务之间又需要先后依赖的关系。针对这样的处理逻辑, 你可以使用Task ContinueWith 来保证任务的执行顺序。

    建议:再系统中,需要避免你类似的无限点击创建多任务情况,需要加一些控制来保证程序的稳定。

    下面例子仅供参考。

            Task temp;
            private async void Btn1_Click(object sender, RoutedEventArgs e)   //问题出现在这,当这个异步任务执行时,再发起一个这个任务,程序不会等待老任务结束而立刻发起新的任务
            {
                // task Task
                if (temp == null)
                {
                    temp = Task.Factory.StartNew(() =>
                    {
                        for (int i = 0; i < 3; i++)
                        {
                            Thread.Sleep(1000);
                            Console.WriteLine(" I : " + i.ToString() + " ,ThreadId: " + Thread.CurrentThread.ManagedThreadId.ToString());
                        }
                    });
                }
                else
                {
                    var tasks = temp.ContinueWith((ant) =>
                     {
                         for (int i = 0; i < 3; i++)
                         {
                             Thread.Sleep(1000);
                             Console.WriteLine("I : " + i.ToString() + " ,ThreadId: " + Thread.CurrentThread.ManagedThreadId.ToString());
                         }
                     });
                    temp = tasks;
                }
    
    
       }
    


    你也可以尝试使用使用了ThreadPool,ManualResetEvent: 参考:C#多线程顺序依赖执行控制 


    Task Class 


    Best Regards,

    Yong Lu


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    • 已标记为答案 CZAupup 2019年1月4日 17:03
    2019年1月4日 3:19
    版主
  • 可以尝试互斥锁或者类似同步线程技术

    参考

    线程处理


    我不太清楚你代码的具体问题 我按照我的经验写了一个小小的例子

     public class Test : INotifyPropertyChanged
        {
            private int _plus;
    
            public int Plus { get => _plus; set { _plus = value;  OnChanged(new PropertyChangedEventArgs ("Plus")); } }
    
            public event PropertyChangedEventHandler PropertyChanged;
    
            protected void OnChanged(PropertyChangedEventArgs args) => PropertyChanged?.Invoke(this, args);
    
        }
    
        /// <summary>
        /// MainWindow.xaml 的交互逻辑
        /// </summary>
        public partial class MainWindow : Window
        {
            Test test;
    
            public MainWindow()
            {
                InitializeComponent();
                test = new Test();
                Tb.DataContext = test;
                test .Plus= 1;
            }
            
            private async void Button_Click(object sender, RoutedEventArgs e)
            {
                await Task.Run(()=> 
                {
                  var t=  new Thd(Plus);
                    t.Start();  
                    var t2 = new Thd(Sub);
                    t2.Start();
                });
            }
           //减法
            private void Sub()
            {
                for (var i = 0; i < 100000; i++)
                    test.Plus -=1;
            }
            //加法
            private void Plus()
            {
                for (var i = 0; i < 100000; i++)
                    test.Plus+=1;
            }
        }


    针对这个问题 最简单的是使用Jion方法

      private async void Button_Click(object sender, RoutedEventArgs e)
            {
                await Task.Run(()=> 
                {
                  var t=  new Thd(Plus);
                    t.Start();
                   //等待当前线程完成操作
                    t.Join();
                    var t2 = new Thd(Sub);
                    t2.Start();
                });
            }




    兄弟这个JOIN的方法我也试过了,可问题是,只有在确定了异步任务的数量的情况下,才可以使用JOIN。在我的情况下,任务的数量不是固定写在程序里的,所以JOIN不能满足我的需求。楼下给出了一个很好的解决方案,可以参考下哈
    2019年1月4日 17:12