wpf 多线程

一、线程概述:【引用MSDN】

通常,WPF 应用程序从两个线程开始:一个用于处理呈现,一个用于管理 UI。呈现线程有效地隐藏在后台运行,而 UI 线程则接收输入、处理事件、绘制屏幕以及运行应用程序代码。大多数应用程序都使用一个 UI 线程,但在某些情况下,最好使用多个线程。我们将在后面举例说明这一点。

UI 线程对一个名为Dispatcher的对象内的工作项进行排队。Dispatcher基于优先级选择工作项,并运行每一个工作项,直到完成。每个 UI 线程都必须至少有一个Dispatcher,并且每个Dispatcher都只能在一个线程中执行工作项。

要构建响应速度快、且用户友好的应用程序,诀窍是减小工作项,以最大限度地提高Dispatcher吞吐量。这样,工作项将永远不会因为在Dispatcher队列中等待处理而失效。输入与响应之间的任何可察觉的延迟都会使用户不快。

那么,WPF 应用程序应如何处理大型操作呢?如果您的代码涉及大型计算,或者需要查询某台远程服务器上的数据库,应怎么办呢?通常的办法是在单独的线程中处理大型操作,而专门让 UI 线程来负责处理Dispatcher队列中的工作项。当大型操作完成时,可以将结果报告给 UI 线程来显示。

一直以来,Windows 只允许创建 UI 元素的线程访问这些元素。这意味着负责某项长时间运行任务的后台线程无法更新已完成的文本框。Windows 这样做是为了确保 UI 组件的完整性。如果列表框的内容在绘制过程中被后台线程更新,那么该列表框看上去将会很奇怪。

WPF 使用一种内置互斥机制来强制执行这种协调。WPF 中的大多数类都派生自DispatcherObjectDispatcherObject在构造时存储对链接到当前所运行线程的Dispatcher的引用。实际上,DispatcherObject与创建它的线程关联。在程序执行过程中,DispatcherObject可以调用它的公共VerifyAccess方法。VerifyAccess检查与当前线程关联的Dispatcher,并将它与构造过程中存储的Dispatcher引用进行比较。如果两者不匹配,VerifyAccess将引发异常。VerifyAccess用于在每个属于DispatcherObject的方法的开头调用。

如果只有一个线程可以修改 UI,那么后台线程如何与用户交互呢?后台线程可以请求 UI 线程代表它执行操作。这是通过向 UI 线程的Dispatcher注册工作项来完成的。Dispatcher类提供两个注册工作项的方法:InvokeBeginInvoke。这两个方法均调度一个委托来执行。Invoke是同步调用,也就是说,直到 UI 线程实际执行完该委托它才返回。BeginInvoke是异步的,将立即返回。

Dispatcher按优先级对其队列中的元素进行排序。Dispatcher队列中添加元素时可指定 10 个级别。这些优先级在DispatcherPriority枚举中维护。有关DispatcherPriority级别的详细信息可以在 Windows SDK 文档中找到。

二、下面是线程的一个小例子

<Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <StackPanel Grid.Row="0" Orientation="Horizontal" VerticalAlignment="Center" >
        <Button Content="StartWithThread"  
            Click="StartOrStop"
            Name="startStopButton"
            Margin="5,0,5,0"
            />
        <TextBlock Margin="10,5,0,0">Biggest Prime Found:</TextBlock>
        <TextBlock Name="bigPrime" Margin="4,5,0,0">3</TextBlock>
    </StackPanel>

        <StackPanel Grid.Row="1" Orientation="Horizontal" VerticalAlignment="Center" >
        <Button Content="MessageBox.show"  

            Name="startStopButton2"
            Margin="5,0,5,0" Click="startStopButton2_Click" />
        </StackPanel>
        <StackPanel Grid.Row="2" Orientation="Horizontal" VerticalAlignment="Center" >
            <Button Content="StartWithoutThread"     
            Name="startStopButton3"
            Margin="5,0,5,0" Click="startStopButton3_Click" />
            <TextBlock Margin="10,5,0,0">data:</TextBlock>
            <TextBlock Name="myData" Margin="4,5,0,0">3</TextBlock>
        </StackPanel>
    </Grid>

后台代码:

/// <summary>
    /// Interaction logic for ThreadTest.xaml
    /// </summary>
    public partial class ThreadTest : Window
    {
        public delegate void NextPrimeDelegate();

        //Current number to check 
        private long num = 3;

        private bool continueCalculating = false;


        public ThreadTest()
        {
            InitializeComponent();
        }


        private void StartOrStop(object sender, RoutedEventArgs e)
        {
            if (continueCalculating)
            {
                continueCalculating = false;
                startStopButton.Content = "Resume";
            }
            else
            {
                continueCalculating = true;
                startStopButton.Content = "Stop";
                startStopButton.Dispatcher.BeginInvoke(
                    DispatcherPriority.Normal,
                    new NextPrimeDelegate(CheckNextNumber));
            }
        }

        public void CheckNextNumber()
        {
            // Reset flag.
            NotAPrime = false;

            for (long i = 3; i <= Math.Sqrt(num); i++)
            {
                if (num % i == 0)
                {
                    // Set not a prime flag to true.
                    NotAPrime = true;
                    break;
                }
            }

            // If a prime number.
            if (!NotAPrime)
            {
                bigPrime.Text = num.ToString();
            }

            num += 2;
            if (continueCalculating)
            {
                startStopButton.Dispatcher.BeginInvoke(
                    System.Windows.Threading.DispatcherPriority.SystemIdle,
                    new NextPrimeDelegate(this.CheckNextNumber));
            }
        }

        private bool NotAPrime = false;

        private void startStopButton2_Click(object sender, RoutedEventArgs e)
        {
            MessageBox.Show("Hello Thread");
        }

        private void startStopButton3_Click(object sender, RoutedEventArgs e)
        {
           long n = 0;

            // If a prime number.
            while (n < 10000000)
            {

                myData.Text = n.ToString();
                n++;
            }


        }

    }

效果图1:当运行"Stop"按钮时,在单击“MessageBox.show"按钮,则能弹出窗口HelloThead。

wpf 多线程

效果图2。当单击“StartWithoutThrad"按钮时,此时再单击“MessageBox.show"按钮,则提示Not Responding。因为此时没有用到线程。

wpf 多线程

参考资料:http://msdn.microsoft.com/zh-cn/library/ms741870.aspx#threading_overview

三、 下面是使用线程的三种方式

原文:http://www.cnblogs.com/iupme/archive/2011/05/18/2049949.html

第1种用 Task类. 推荐用这个办法
publicvoid工作_Task()

{

Dispatcher x
=Dispatcher.CurrentDispatcher;//取得当前工作线程

//另开线程工作

Task<int>计数=newTask<int>(()=>{return计数方法(); });

计数.ContinueWith(工作完毕后方法);
//工作完毕后执行的方法

计数.Start();//开始工作



}

publicvoid工作完毕后方法(Task<int>参数)

{

if(参数.IsCompleted)//正常工作完毕

{

var 结果
=参数.Result;//取得结果

//处理结果.

//本方法非界面线程.如果需要在界面线程操作,需要转移到界面线程

}

}



intc;

publicint计数方法()

{

returnc++;

}

第2种方法用线程.
publicvoid工作_Thread()

{

Dispatcher x
=Dispatcher.CurrentDispatcher;//取得当前工作线程

//另开线程工作

System.Threading.ThreadStart start=delegate()

{

//工作函数

Func<string>fu=newFunc<string>(()=>{return""; });//工作函数

var 工作结果=fu();//开始工作



//异步更新界面

x.BeginInvoke(newAction(()=>

{

//在界面线程操作 可以使用 工作结果

}), DispatcherPriority.Normal);

};

newSystem.Threading.Thread(start).Start();//启动线程

}
第3种方法用 BackgroundWorker.

这种方法介绍的比较多了.就不说了.
BackgroundWorker 后台线程;

publicvoid线程初始化()

{

后台线程
=newBackgroundWorker();

后台线程.WorkerSupportsCancellation
=true;//可以取消

后台线程.DoWork+=newDoWorkEventHandler(后台线程_DoWork);

后台线程.RunWorkerCompleted
+=newRunWorkerCompletedEventHandler(后台线程_RunWorkerCompleted);

}

publicvoid启动后台线程()

{

后台线程.RunWorkerAsync();

}



void后台线程_RunWorkerCompleted(objectsender, RunWorkerCompletedEventArgs e)

{

//工作完毕的方法

}



void后台线程_DoWork(objectsender, DoWorkEventArgs e)

{

//工作方法

}

原文链接: https://www.cnblogs.com/linlf03/archive/2011/09/01/2043139.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/31570

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月8日 上午8:51
下一篇 2023年2月8日 上午8:53

相关推荐