重新想象 Windows 8 Store Apps (46) - 多线程之线程同步: Lock, Monitor, Interlocked, Mutex, ReaderWriterLock

简介: 原文:重新想象 Windows 8 Store Apps (46) - 多线程之线程同步: Lock, Monitor, Interlocked, Mutex, ReaderWriterLock[源码下载] 重新想象 Windows 8 Store Apps (46) - 多线程之线程同步: L...
原文: 重新想象 Windows 8 Store Apps (46) - 多线程之线程同步: Lock, Monitor, Interlocked, Mutex, ReaderWriterLock

[源码下载]


重新想象 Windows 8 Store Apps (46) - 多线程之线程同步: Lock, Monitor, Interlocked, Mutex, ReaderWriterLock



作者:webabcd


介绍
重新想象 Windows 8 Store Apps 之 线程同步

  • lock - 其实就是对 Monitor.Enter() 和 Monitor.Exit() 的一个封装
  • Monitor - 锁
  • Interlocked - 为多个线程共享的数字型变量提供原子操作
  • Mutex - 互斥锁,主要用于同一系统内跨进程的互斥锁
  • ReaderWriterLock - 读写锁



示例
1、演示 lock 的使用
Thread/Lock/LockDemo.xaml

<Page
    x:Class="XamlDemo.Thread.Lock.LockDemo"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:XamlDemo.Thread.Lock"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="Transparent">
        <StackPanel Margin="120 0 0 0">

            <TextBlock Name="lblMsgWithoutLock" FontSize="14.667" />
            <TextBlock Name="lblMsgWithLock" FontSize="14.667" />

        </StackPanel>
    </Grid>
</Page>

Thread/Lock/LockDemo.xaml.cs

/*
 * 演示 lock 的使用
 * 
 * 注:lock 其实就是对 Monitor.Enter() 和 Monitor.Exit() 的一个封装
 */

using System.Collections.Generic;
using System.Threading.Tasks;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;

namespace XamlDemo.Thread.Lock
{
    public sealed partial class LockDemo : Page
    {
        // 需要被 lock 的对象
        private static readonly object _objLock = new object();

        private static int _countWithoutLock;
        private static int _countWithLock;

        public LockDemo()
        {
            this.InitializeComponent();
        }

        protected async override void OnNavigatedTo(NavigationEventArgs e)
        {
            List<Task> tasks = new List<Task>();

            // 一共 100 个任务并行执行,每个任务均累加同一个静态变量 100000 次,以模拟并发访问静态变量的场景
            for (int i = 0; i < 100; i++)
            {
                Task task = Task.Run(
                    () =>
                    {
                        /******************有锁的逻辑开始******************/
                        try
                        {
                            // 通过 lock 锁住指定的对象以取得排它锁,在 lock 区域内的代码执行完毕后释放排它锁,排它锁释放之前其它进入到此的线程会排队等候
                            lock (_objLock)
                            {
                                for (int j = 0; j < 100000; j++)
                                {
                                    _countWithLock++;
                                }
                            }
                        }
                        finally { }
                        /******************有锁的逻辑结束******************/


                        /******************没锁的逻辑开始******************/
                        for (int j = 0; j < 100000; j++)
                        {
                            _countWithoutLock++;
                        }
                        /******************没锁的逻辑结束******************/
                    });

                tasks.Add(task);
            }

            // 等待所有任务执行完毕
            await Task.WhenAll(tasks);

            lblMsgWithoutLock.Text = "计数器(不带锁)结果:" + _countWithoutLock.ToString();
            lblMsgWithLock.Text = "计数器(带锁)结果:" + _countWithLock.ToString();
        }
    }
}


2、演示 Monitor 的使用
Thread/Lock/MonitorDemo.xaml

<Page
    x:Class="XamlDemo.Thread.Lock.MonitorDemo"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:XamlDemo.Thread.Lock"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="Transparent">
        <StackPanel Margin="120 0 0 0">

            <TextBlock Name="lblMsg" FontSize="14.667" />

        </StackPanel>
    </Grid>
</Page>

Thread/Lock/MonitorDemo.xaml.cs

/*
 * 演示 Monitor 的使用
 * 
 * 本例说明:
 * 由于 Task 基于线程池,所以 task1 和 task2 的启动顺序是不一定的,以下步骤假定 task1 先执行,task2 后执行
 * 1、task1 取得排它锁
 * 2、task1 Monitor.Wait() - 释放排它锁,然后 task1 进入等待队列,可以为其指定一个超时时间,超过则进入就绪队列
 * 3、task2 取得排它锁
 * 4、task2 Monitor.Pulse() - 让等待队列中的一个线程进入就绪队列(Monitor.PulseAll() 的作用是将等待队列中的全部线程全部放入就绪队列)
 * 5、task1 进入就绪队列
 * 6、task2 Monitor.Wait() - 释放排它锁,然后 task2 进入等待队列
 * 7、task1 取得排它锁
 * 8、以上步骤不断往复
 * 
 * 注:
 * 1、Wait() 和 Pulse() 必须在 Enter() 和 Exit() 之间,或者在 lock(){ } 中
 * 2、只有就绪队列中的线程才能取得排它锁,等待队列中的线程是无法取得排它锁的
 */

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;

namespace XamlDemo.Thread.Lock
{
    public sealed partial class MonitorDemo : Page
    {
        // 需要被 lock 的对象
        private static readonly object _objLock = new object();

        public MonitorDemo()
        {
            this.InitializeComponent();
        }

        protected async override void OnNavigatedTo(NavigationEventArgs e)
        {
            string result = "";

            // 在 task1 中执行则为 true,在 task2 中执行则为 false
            bool flag = true;

            Task task1 = Task.Run(
                () =>
                {
                    try
                    {
                        // 在指定的对象上取得排它锁
                        Monitor.Enter(_objLock);

                        for (int i = 0; i < 10; i++)
                        {
                            if (flag)
                                Monitor.Wait(_objLock); 

                            flag = true;

                            result += string.Format("task1 i:{0}, taskId:{1}", i, Task.CurrentId);
                            result += Environment.NewLine;

                            Monitor.Pulse(_objLock);
                        }
                    }
                    finally
                    {
                        // 在指定的对象上释放排它锁
                        Monitor.Exit(_objLock);
                    }
                });

            Task task2 = Task.Run(
                () =>
                {
                    try
                    {
                        // 在指定的对象上取得排它锁
                        Monitor.Enter(_objLock);

                        for (int i = 0; i < 10; i++)
                        {
                            if (!flag)
                                Monitor.Wait(_objLock);

                            flag = false;

                            result += string.Format("task2 i:{0}, taskId:{1}", i, Task.CurrentId);
                            result += Environment.NewLine;

                            Monitor.Pulse(_objLock);
                        }
                    }
                    finally
                    {
                        // 在指定的对象上释放排它锁
                        Monitor.Exit(_objLock);
                    }
                });

            await Task.WhenAll(task1, task2);

            lblMsg.Text = result;
        }
    }
}


3、演示 Interlocked 的使用
Thread/Lock/InterlockedDemo.xaml

<Page
    x:Class="XamlDemo.Thread.Lock.InterlockedDemo"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:XamlDemo.Thread.Lock"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="Transparent">
        <StackPanel Margin="120 0 0 0">

            <TextBlock Name="lblMsgWithoutLock" FontSize="14.667" />
            <TextBlock Name="lblMsgWithLock" FontSize="14.667" />

        </StackPanel>
    </Grid>
</Page>

Thread/Lock/InterlockedDemo.xaml.cs

/*
 * 演示 Interlocked 的使用
 * 
 * Interlocked - 为多个线程共享的数字型变量提供原子操作,其提供了各种原子级的操作方法,如:增减变量、比较变量、指定变量的值
 * 
 * 注:
 * long Read(ref long location) - 用于在 32 位系统上以原子方式读取 64 位值(32 位系统访问 32 位值本身就是原子的,64 位系统访问 64 位值本身就是原子的)
 */

using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;

namespace XamlDemo.Thread.Lock
{
    public sealed partial class InterlockedDemo : Page
    {
        private static int _countWithoutLock;
        private static int _countWithLock;

        public InterlockedDemo()
        {
            this.InitializeComponent();
        }

        protected async override void OnNavigatedTo(NavigationEventArgs e)
        {
            List<Task> tasks = new List<Task>();

            // 一共 100 个任务并行执行,每个任务均累加同一个静态变量 100000 次,以模拟并发访问静态变量的场景
            for (int i = 0; i < 100; i++)
            {
                Task task = Task.Run(
                    () =>
                    {
                        /******************有锁的逻辑开始******************/
                        for (int j = 0; j < 100000; j++)
                        {
                            // 原子方式让 _countWithLock 加 1
                            Interlocked.Increment(ref _countWithLock);
                        }
                        /******************有锁的逻辑结束******************/


                        /******************没锁的逻辑开始******************/
                        for (int j = 0; j < 100000; j++)
                        {
                            _countWithoutLock++;
                        }
                        /******************没锁的逻辑结束******************/
                    });

                tasks.Add(task);
            }

            await Task.WhenAll(tasks);

            lblMsgWithoutLock.Text = "计数器(不带锁)结果:" + _countWithoutLock.ToString();
            lblMsgWithLock.Text = "计数器(带锁)结果:" + _countWithLock.ToString();
        }
    }
}


4、演示 Mutex 的使用
Thread/Lock/MutexDemo.xaml

<Page
    x:Class="XamlDemo.Thread.Lock.MutexDemo"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:XamlDemo.Thread.Lock"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="Transparent">
        <StackPanel Margin="120 0 0 0">

            <TextBlock Name="lblMsgWithoutLock" FontSize="14.667" />
            <TextBlock Name="lblMsgWithLock" FontSize="14.667" />

        </StackPanel>
    </Grid>
</Page>

Thread/Lock/MutexDemo.xaml.cs

/*
 * 演示 Mutex 的使用
 * 
 * Mutex - 互斥锁,主要用于同一系统内跨进程的互斥锁
 */

using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;

namespace XamlDemo.Thread.Lock
{
    public sealed partial class MutexDemo : Page
    {
        private Mutex _mutex = new Mutex();

        private static int _countWithoutLock;
        private static int _countWithLock;

        public MutexDemo()
        {
            this.InitializeComponent();
        }

        protected async override void OnNavigatedTo(NavigationEventArgs e)
        {
            List<Task> tasks = new List<Task>();

            // 一共 100 个任务并行执行,每个任务均累加同一个静态变量 100000 次,以模拟并发访问静态变量的场景
            for (int i = 0; i < 100; i++)
            {
                Task task = Task.Run(
                    () =>
                    {
                        /******************有锁的逻辑开始******************/
                        // 当前线程拿到 Mutex,阻塞当前线程,可以指定阻塞的超时时间
                        _mutex.WaitOne(); 

                        for (int j = 0; j < 100000; j++)
                        {
                            _countWithLock++;
                        }

                        // 释放 Mutex
                        _mutex.ReleaseMutex();
                        /******************有锁的逻辑结束******************/


                        /******************没锁的逻辑开始******************/
                        for (int j = 0; j < 100000; j++)
                        {
                            _countWithoutLock++;
                        }
                        /******************没锁的逻辑结束******************/
                    });

                tasks.Add(task);
            }

            await Task.WhenAll(tasks);

            lblMsgWithoutLock.Text = "计数器(不带锁)结果:" + _countWithoutLock.ToString();
            lblMsgWithLock.Text = "计数器(带锁)结果:" + _countWithLock.ToString();
        }
    }
}


5、演示 ReaderWriterLockSlim 的使用
Thread/Lock/ReaderWriterLockDemo.xaml

<Page
    x:Class="XamlDemo.Thread.Lock.ReaderWriterLockDemo"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:XamlDemo.Thread.Lock"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="Transparent">
        <StackPanel Margin="120 0 0 0">

            <TextBlock Name="lblMsgForRead" FontSize="14.667" />
            <TextBlock Name="lblMsgForWrite" FontSize="14.667" />

        </StackPanel>
    </Grid>
</Page>

Thread/Lock/ReaderWriterLockDemo.xaml.cs

/*
 * 演示 ReaderWriterLockSlim 的使用
 * 
 * ReaderWriterLock - 读写锁(WinRT 中不提供)
 * ReaderWriterLockSlim - 轻量级的 ReaderWriterLock
 *     支持进入/离开读锁,进入/离开写锁,读锁升级为写锁
 *     支持相关状态的获取,如:当前线程是否进入了读锁以及进入读锁的次数,是否进入了写锁以及进入写锁的次数,是否由读锁升级为了写锁以及由读锁升级为写锁的次数
 * 
 * 注:
 * 1、每次可以有多个线程进入读锁
 * 2、每次只能有一个线程进入写锁
 * 3、进入写锁后,无法进入读锁
 * 
 * 
 * 本例模拟了一个“高频率读,低频率写”的场景
 */

using System;
using System.Threading;
using Windows.System.Threading;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;

namespace XamlDemo.Thread.Lock
{
    public sealed partial class ReaderWriterLockDemo : Page
    {
        ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim();

        public ReaderWriterLockDemo()
        {
            this.InitializeComponent();
        }

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            ThreadPoolTimer.CreatePeriodicTimer(
                (timer) =>
                {
                    // 进入读锁
                    _rwLock.EnterReadLock();

                    OutMsgForRead("读:" + DateTime.Now.ToString("mm:ss.fff"));

                    // 离开读锁
                    _rwLock.ExitReadLock();
                },
                TimeSpan.FromMilliseconds(100));


            ThreadPoolTimer.CreatePeriodicTimer(
               (timer) =>
               {
                   // 进入写锁
                   _rwLock.EnterWriteLock();

                   new ManualResetEvent(false).WaitOne(3000); // 本线程停 3000 毫秒
                   OutMsgForWrite("写:" + DateTime.Now.ToString("mm:ss.fff"));

                   // 离开写锁
                   _rwLock.ExitWriteLock();
               },
               TimeSpan.FromMilliseconds(5000));
        }

        private async void OutMsgForRead(string msg)
        {
            await Dispatcher.RunAsync(
                Windows.UI.Core.CoreDispatcherPriority.High,
                () =>
                {
                    lblMsgForRead.Text = msg;
                });
        }

        private async void OutMsgForWrite(string msg)
        {
            await Dispatcher.RunAsync(
                Windows.UI.Core.CoreDispatcherPriority.High,
                () =>
                {
                    lblMsgForWrite.Text = msg;
                });
        }
    }
}



OK
[源码下载]

目录
相关文章
|
11月前
|
安全 算法 Java
Java 多线程:线程安全与同步控制的深度解析
本文介绍了 Java 多线程开发的关键技术,涵盖线程的创建与启动、线程安全问题及其解决方案,包括 synchronized 关键字、原子类和线程间通信机制。通过示例代码讲解了多线程编程中的常见问题与优化方法,帮助开发者提升程序性能与稳定性。
439 0
|
11月前
|
数据采集 监控 调度
干货分享“用 多线程 爬取数据”:单线程 + 协程的效率反超 3 倍,这才是 Python 异步的正确打开方式
在 Python 爬虫中,多线程因 GIL 和切换开销效率低下,而协程通过用户态调度实现高并发,大幅提升爬取效率。本文详解协程原理、实战对比多线程性能,并提供最佳实践,助你掌握异步爬虫核心技术。
|
Java 数据挖掘 调度
Java 多线程创建零基础入门新手指南:从零开始全面学习多线程创建方法
本文从零基础角度出发,深入浅出地讲解Java多线程的创建方式。内容涵盖继承`Thread`类、实现`Runnable`接口、使用`Callable`和`Future`接口以及线程池的创建与管理等核心知识点。通过代码示例与应用场景分析,帮助读者理解每种方式的特点及适用场景,理论结合实践,轻松掌握Java多线程编程 essentials。
798 5
|
Python
python3多线程中使用线程睡眠
本文详细介绍了Python3多线程编程中使用线程睡眠的基本方法和应用场景。通过 `time.sleep()`函数,可以使线程暂停执行一段指定的时间,从而控制线程的执行节奏。通过实际示例演示了如何在多线程中使用线程睡眠来实现计数器和下载器功能。希望本文能帮助您更好地理解和应用Python多线程编程,提高程序的并发能力和执行效率。
584 20
|
安全 Java C#
Unity多线程使用(线程池)
在C#中使用线程池需引用`System.Threading`。创建单个线程时,务必在Unity程序停止前关闭线程(如使用`Thread.Abort()`),否则可能导致崩溃。示例代码展示了如何创建和管理线程,确保在线程中执行任务并在主线程中处理结果。完整代码包括线程池队列、主线程检查及线程安全的操作队列管理,确保多线程操作的稳定性和安全性。
|
NoSQL Redis
单线程传奇Redis,为何引入多线程?
Redis 4.0 引入多线程支持,主要用于后台对象删除、处理阻塞命令和网络 I/O 等操作,以提高并发性和性能。尽管如此,Redis 仍保留单线程执行模型处理客户端请求,确保高效性和简单性。多线程仅用于优化后台任务,如异步删除过期对象和分担读写操作,从而提升整体性能。
339 1
|
C# Windows
【Azure App Service】在App Service for Windows上验证能占用的内存最大值
根据以上测验,当使用App Service内存没有达到预期的值,且应用异常日志出现OutOfMemory时,就需要检查Platform的设置是否位64bit。
347 11
|
Java 开发者
在Java多线程编程中,选择合适的线程创建方法至关重要
【10月更文挑战第20天】在Java多线程编程中,选择合适的线程创建方法至关重要。本文通过案例分析,探讨了继承Thread类和实现Runnable接口两种方法的优缺点及适用场景,帮助开发者做出明智的选择。
212 2
|
Java
Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口
【10月更文挑战第20天】《JAVA多线程深度解析:线程的创建之路》介绍了Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口。文章详细讲解了每种方式的实现方法、优缺点及适用场景,帮助读者更好地理解和掌握多线程编程技术,为复杂任务的高效处理奠定基础。
299 2
|
数据采集 Java Python
爬取小说资源的Python实践:从单线程到多线程的效率飞跃
本文介绍了一种使用Python从笔趣阁网站爬取小说内容的方法,并通过引入多线程技术大幅提高了下载效率。文章首先概述了环境准备,包括所需安装的库,然后详细描述了爬虫程序的设计与实现过程,包括发送HTTP请求、解析HTML文档、提取章节链接及多线程下载等步骤。最后,强调了性能优化的重要性,并提醒读者遵守相关法律法规。
620 0

热门文章

最新文章