重新想象 Windows 8 Store Apps (47) - 多线程之线程同步: Semaphore, CountdownEvent, Barrier, ManualResetEvent, AutoResetEvent

本文涉及的产品
云原生内存数据库 Tair,内存型 2GB
云数据库 Redis 版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: 原文:重新想象 Windows 8 Store Apps (47) - 多线程之线程同步: Semaphore, CountdownEvent, Barrier, ManualResetEvent, AutoResetEvent[源码下载] 重新想象 Windows 8 Store Apps (...
原文: 重新想象 Windows 8 Store Apps (47) - 多线程之线程同步: Semaphore, CountdownEvent, Barrier, ManualResetEvent, AutoResetEvent

[源码下载]


重新想象 Windows 8 Store Apps (47) - 多线程之线程同步: Semaphore, CountdownEvent, Barrier, ManualResetEvent, AutoResetEvent



作者:webabcd


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

  • Semaphore - 信号量
  • CountdownEvent - 通过信号数量实现线程同步
  • Barrier - 屏障
  • ManualResetEvent - 手动红绿灯
  • AutoResetEvent - 自动红绿灯



示例
1、演示 Semaphore 的使用
Thread/Lock/SemaphoreDemo.xaml

<Page
    x:Class="XamlDemo.Thread.Lock.SemaphoreDemo"
    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/SemaphoreDemo.xaml.cs

/*
 * 演示 Semaphore 的使用
 * 
 * Semaphore - 信号量
 * SemaphoreSlim - 轻量级的 Semaphore
 * 
 * 注:
 * 直译 Semaphore 的话不太好理解,可以将 Semaphore 理解为一个许可证中心,该许可证中心的许可证数量是有限的
 * 线程想要执行就要先从许可证中心获取一个许可证(如果许可证中心的许可证已经发完了,那就等着,等着其它线程归还许可证),执行完了再还回去
 */

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 SemaphoreDemo : Page
    {
        /*
         * Semaphore(int initialCount, int maximumCount, string name)
         *     initialCount - 许可证中心初始拥有的许可证数量,即初始情况下已经发出的许可证数量为 maximumCount - initialCount
         *     maximumCount - 许可证中心总共拥有的许可证数量
         *     name - 许可证中心的名称
         * Semaphore OpenExisting(string name) - 打开指定名称的许可证中心
         */

        // 实例化一个许可证中心,该中心拥有的许可证数量为 2 个
        private Semaphore _semaphore = new Semaphore(2, 2);

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

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

            // 模拟 5 个线程并行执行,拿到许可证的线程才能运行,而许可证中心只有 2 个许可证
            for (int i = 0; i < 5; i++)
            {
                CancellationToken token = new CancellationTokenSource().Token;

                Task task = Task.Run(
                    () =>
                    {
                        OutMsg(string.Format("task {0} 等待一个许可证", Task.CurrentId));
                        token.WaitHandle.WaitOne(5000);

                        // WaitOne() - 申请许可证
                        _semaphore.WaitOne();
                        OutMsg(string.Format("task {0} 申请到一个许可证", Task.CurrentId));

                        token.WaitHandle.WaitOne(1000);

                        OutMsg(string.Format("task {0} 归还了一个许可证", Task.CurrentId));
                        // int Release() - 归还许可证,返回值为:Release() 之前许可证中心可用的许可证数量
                        int ignored = _semaphore.Release();
                        // int Release(int releaseCount) - 指定释放的信号量的次数(按本文的理解就是指定归还的许可证数量)
                    },
                    token);

                tasks.Add(task);
            }

            await Task.WhenAll(tasks);
        }

        private void OutMsg(string msg)
        {
            var ignored = Dispatcher.RunAsync(
                Windows.UI.Core.CoreDispatcherPriority.High, 
                () => 
                {
                    lblMsg.Text += msg;
                    lblMsg.Text += Environment.NewLine;
                });
        }
    }
}


2、演示 CountdownEvent 的使用
Thread/Lock/CountdownEventDemo.xaml

<Page
    x:Class="XamlDemo.Thread.Lock.CountdownEventDemo"
    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/CountdownEventDemo.xaml.cs

/*
 * 演示 CountdownEvent 的使用
 * 
 * CountdownEvent - 通过信号数量实现线程同步
 */

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 CountdownEventDemo : Page
    {
        private static int _count;

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

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            // 初始信号数量为 100 个
            using (CountdownEvent countdown = new CountdownEvent(100))
            {
                // AddCount(), AddCount(int signalCount) - 增加 1 个信号,或增加指定数量的信号
                // Reset(), Reset(int count) - 重置为初始的信号数量,或重置为指定的信号数量
                // Signal(), Signal(int signalCount) - 减少 1 个信号,或减少指定数量的信号
                // CurrentCount - 获取当前的信号数量

                for (int i = 0; i < 100; i++)
                {
                    Task task = Task.Run(
                        () =>
                        {
                            Interlocked.Increment(ref _count);
                            // 减少 1 个信号
                            countdown.Signal();
                        });
                }

                // 阻塞当前线程,直到 CountdownEvent 的信号数量变为 0
                countdown.Wait();

                lblMsg.Text = "count: " + _count.ToString();
            }
        }
    }
}


3、演示 Barrier 的使用
Thread/Lock/BarrierDemo.xaml

<Page
    x:Class="XamlDemo.Thread.Lock.BarrierDemo"
    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/BarrierDemo.xaml.cs

/*
 * 演示 Barrier 的使用
 * 
 * Barrier - 屏障
 * 
 * 按如下方式理解:
 * 1、Participant - 参与者
 * 2、SignalAndWait() - 某一个参与者已经到达屏障了
 * 3、所有参与者都到达屏障后,屏障解除
 */

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

namespace XamlDemo.Thread.Lock
{
    public sealed partial class BarrierDemo : Page
    {
        private static int _count;

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

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            // AddParticipant(), AddParticipants(int participantCount) - 增加 1 个参与者,或增加指定数量的参与者
            // RemoveParticipant(), RemoveParticipants(int participantCount) - 减少 1 个参与者,或减少指定数量的参与者
            // ParticipantCount - 获取参与者总数
            // ParticipantsRemaining - 尚未到达屏障的参与者总数

            Barrier barrier = new Barrier(
                5, // 初始有 5 个参与者
                (ctx) => // 屏障解除之后
                {
                    var ignored = Dispatcher.RunAsync(CoreDispatcherPriority.High,
                        () =>
                        {
                            lblMsg.Text = "count: " + _count.ToString();
                        });
                });

            for (int i = 0; i < 5; i++)
            {
                Task task = Task.Run(
                    () =>
                    {
                        Interlocked.Increment(ref _count);
                        // 某一个参与者已经到达屏障了
                        barrier.SignalAndWait();
                        // SignalAndWait(int millisecondsTimeout) - 指定一个超时时间
                        // SignalAndWait(CancellationToken cancellationToken) - 指定一个 CancellationToken
                    });
            }
        }
    }
}


4、演示 ManualResetEvent 的使用
Thread/Lock/ManualResetEventDemo.xaml

<Page
    x:Class="XamlDemo.Thread.Lock.ManualResetEventDemo"
    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/ManualResetEventDemo.xaml.cs

/*
 * 演示 ManualResetEvent 的使用
 * 
 * ManualResetEvent - 手动红绿灯
 * ManualResetEventSlim - 轻量级的 ManualResetEvent
 */

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

namespace XamlDemo.Thread.Lock
{
    public sealed partial class ManualResetEventDemo : Page
    {
        // true - 指定初始状态为绿灯
        private ManualResetEvent _manualResetEvent = new ManualResetEvent(true);

        private static int _count = 0;

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

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            ManualResetEvent sleep = new ManualResetEvent(false);

            Task tas = Task.Run(
                () =>
                {
                    while (true)
                    {
                        // WaitOne() - 判断:绿灯则进入,红灯则阻塞
                        _manualResetEvent.WaitOne();

                        /*
                         * WaitOne(1000) 
                         *     1、如果当前是绿灯则进入
                         *     2、如果当前是红灯则阻塞
                         *         a) 1000 毫秒之内收到 Set() 信号则进入
                         *         b) 1000 毫秒之后如果还没收到 Set() 信号则强行进入
                         */

                        IAsyncAction ignored = Windows.System.Threading.ThreadPool.RunAsync(
                            (threadPoolWorkItem) =>
                            {
                                // 在当前线程 sleep 1000 毫秒(WinRT 中没有 Thread.Sleep() 了)
                                sleep.WaitOne(1000);

                                OutMsg(string.Format("count:{0}, time:{1}", ++_count, DateTime.Now.ToString("mm:ss")));

                                // Set() - 发出绿灯信号,并设置为绿灯
                                _manualResetEvent.Set();

                            },
                            WorkItemPriority.High);

                        // Reset() - 发出红灯信号,并设置为红灯
                        _manualResetEvent.Reset(); 
                    }
                });
        }

        private void OutMsg(string msg)
        {
            var ignored = Dispatcher.RunAsync(
                Windows.UI.Core.CoreDispatcherPriority.High,
                () =>
                {
                    lblMsg.Text = msg;
                });
        }
    }
}


5、演示 AutoResetEvent 的使用
Thread/Lock/AutoResetEventDemo.xaml

<Page
    x:Class="XamlDemo.Thread.Lock.AutoResetEventDemo"
    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/AutoResetEventDemo.xaml.cs

/*
 * 演示 AutoResetEvent 的使用
 * 
 * AutoResetEvent - 自动红绿灯
 * 
 * AutoResetEvent 和 ManualResetEvent 的区别在于:AutoResetEvent 在 WaitOne() 进入之后会自动 Reset()
 */

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

namespace XamlDemo.Thread.Lock
{
    public sealed partial class AutoResetEventDemo : Page
    {
        // true - 指定初始状态为绿灯
        private AutoResetEvent _autoResetEvent = new AutoResetEvent(true);

        private static int _count = 0;

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

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            ManualResetEvent sleep = new ManualResetEvent(false);

            Task tas = Task.Run(
                () =>
                {
                    while (true)
                    {
                        // WaitOne() - 判断:绿灯则进入,红灯则阻塞,进入之后自动 Reset()
                        _autoResetEvent.WaitOne();

                        /*
                         * WaitOne(1000) 
                         *     1、如果当前是绿灯则进入,进入之后自动 Reset()
                         *     2、如果当前是红灯则阻塞
                         *         a) 1000 毫秒之内收到 Set() 信号则进入,进入之后自动 Reset()
                         *         b) 1000 毫秒之后如果还没收到 Set() 信号则强行进入,进入之后自动 Reset()
                         */

                        IAsyncAction ignored = Windows.System.Threading.ThreadPool.RunAsync(
                            (threadPoolWorkItem) =>
                            {
                                // 在当前线程 sleep 1000 毫秒(WinRT 中没有 Thread.Sleep() 了)
                                sleep.WaitOne(1000);

                                OutMsg(string.Format("count:{0}, time:{1}", ++_count, DateTime.Now.ToString("mm:ss")));

                                // Set() - 发出绿灯信号,并设置为绿灯
                                _autoResetEvent.Set();

                            },
                            WorkItemPriority.High);
                    }
                });
        }

        private void OutMsg(string msg)
        {
            var ignored = Dispatcher.RunAsync(
                Windows.UI.Core.CoreDispatcherPriority.High,
                () =>
                {
                    lblMsg.Text = msg;
                });
        }
    }
}



OK
[源码下载]

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
23天前
|
Linux C++ Windows
【Azure 应用服务】Azure App Service(Windows)环境中如何让.NET应用调用SAP NetWeaver RFC函数
【Azure 应用服务】Azure App Service(Windows)环境中如何让.NET应用调用SAP NetWeaver RFC函数
【Azure 应用服务】Azure App Service(Windows)环境中如何让.NET应用调用SAP NetWeaver RFC函数
|
5天前
|
Java 数据库 Android开发
一个Android App最少有几个线程?实现多线程的方式有哪些?
本文介绍了Android多线程编程的重要性及其实现方法,涵盖了基本概念、常见线程类型(如主线程、工作线程)以及多种多线程实现方式(如`Thread`、`HandlerThread`、`Executors`、Kotlin协程等)。通过合理的多线程管理,可大幅提升应用性能和用户体验。
24 15
一个Android App最少有几个线程?实现多线程的方式有哪些?
|
7天前
|
Java 数据库 Android开发
一个Android App最少有几个线程?实现多线程的方式有哪些?
本文介绍了Android应用开发中的多线程编程,涵盖基本概念、常见实现方式及最佳实践。主要内容包括主线程与工作线程的作用、多线程的多种实现方法(如 `Thread`、`HandlerThread`、`Executors` 和 Kotlin 协程),以及如何避免内存泄漏和合理使用线程池。通过有效的多线程管理,可以显著提升应用性能和用户体验。
24 10
|
23天前
|
Java 应用服务中间件 开发工具
[App Service for Windows]通过 KUDU 查看 Tomcat 配置信息
[App Service for Windows]通过 KUDU 查看 Tomcat 配置信息
|
23天前
|
Java 应用服务中间件 Windows
【App Service for Windows】为 App Service 配置自定义 Tomcat 环境
【App Service for Windows】为 App Service 配置自定义 Tomcat 环境
|
23天前
|
PHP Windows
【Azure App Service for Windows】 PHP应用出现500 : The page cannot be displayed because an internal server error has occurred. 错误
【Azure App Service for Windows】 PHP应用出现500 : The page cannot be displayed because an internal server error has occurred. 错误
|
24天前
|
PHP 开发工具 git
【Azure 应用服务】在 App Service for Windows 中自定义 PHP 版本的方法
【Azure 应用服务】在 App Service for Windows 中自定义 PHP 版本的方法
|
23天前
|
网络安全 API 数据安全/隐私保护
【Azure App Service】.NET代码实验App Service应用中获取TLS/SSL 证书 (App Service Windows)
【Azure App Service】.NET代码实验App Service应用中获取TLS/SSL 证书 (App Service Windows)
|
23天前
|
Shell PHP Windows
【Azure App Service】Web Job 报错 UNC paths are not supported. Defaulting to Windows directory.
【Azure App Service】Web Job 报错 UNC paths are not supported. Defaulting to Windows directory.
|
23天前
|
存储 Linux Windows
【应用服务 App Service】App Service For Windows 如何挂载Storage Account File Share 示例
【应用服务 App Service】App Service For Windows 如何挂载Storage Account File Share 示例