【转】1.7异步编程:基于事件的异步编程模式(EAP)

传送门:异步编程系列目录……

上一篇,我给大家介绍了“.NET1.0 IAsyncResult异步编程模型(APM)”,通过Begin*** 开启操作并返回IAsyncResult对象,使用 End*** 方法来结束操作,通过回调方法来做异步操作后其它事项。然而最大的问题是没有提供进度通知等功能及多线程间控件的访问。为克服这个问题(并解决其他一些问题),.NET2.0 中引入了:基于事件的异步编程模式(EAP,Event-based Asynchronous Pattern)。通过事件、AsyncOperationManager类和AsyncOperation类两个帮助器类实现如下功能:

1)   异步执行耗时的任务。

2)   获得进度报告和增量结果。

3)   支持耗时任务的取消。

4)   获得任务的结果值或异常信息。

5)   更复杂:支持同时执行多个异步操作、进度报告、增量结果、取消操作、返回结果值或异常信息。

对于相对简单的多线程应用程序,BackgroundWorker组件提供了一个简单的解决方案。对于更复杂的异步应用程序,可以考虑实现一个符合基于事件的异步模式的类。

 

 

 

源码下载:异步编程:基于事件的异步模型(EAP).rar

 

EAP异步编程模型的优点

    EAP是为Windows窗体开发人员创建的,其主要优点在于:

1.   EAP与Microsoft Visual Studio UI设计器进行了很好的集成。也就是说,可将大多数实现了EAP的类拖放到一个Visual Studio设计器平面上,然后双击事件名,让Visual Studio自动生成事件回调方法,并将方法同事件关联起来。

2.   EAP类在内部通过SynchronizationContext类,将应用程序模型映射到合适线程处理模型,以方便跨线程操作控件。

 

为了实现基于事件的异步模式,我们必须先理解两个重要的帮助器类:

 

AsyncOperationManager和AsyncOperation

    AsyncOperationManager类和AsyncOperation类是System.ComponentModel命名空间为我们提供了两个重要帮助器类。在基于事件的异步模式封装标准化的异步功能中,它确保你的异步操作支持在各种应用程序模型(包括 ASP.NET、控制台应用程序和 Windows 窗体应用程序)的适当“线程或上下文”调用客户端事件处理程序。

AsyncOperationManager类和AsyncOperation类的API如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 为支持异步方法调用的类提供并发管理。此类不能被继承。
public static class AsyncOperationManager
{
    // 获取或设置用于异步操作的同步上下文。
    public static SynchronizationContext SynchronizationContext { get; set; }
 
    // 返回可用于对特定异步操作的持续时间进行跟踪的AsyncOperation对象。
    // 参数:userSuppliedState:
    //     一个对象,用于使一个客户端状态(如任务 ID)与一个特定异步操作相关联。
    public static AsyncOperation CreateOperation(object userSuppliedState)
    {
        return AsyncOperation.CreateOperation(userSuppliedState,SynchronizationContext);
    }
}
 
// 跟踪异步操作的生存期。
public sealed class AsyncOperation
{
    // 构造函数
    private AsyncOperation(object userSuppliedState, SynchronizationContext syncContext);
    internal static AsyncOperation CreateOperation(object userSuppliedState
                                            , SynchronizationContext syncContext);
 
    // 获取传递给构造函数的SynchronizationContext对象。
    public SynchronizationContext SynchronizationContext { get; }
    // 获取或设置用于唯一标识异步操作的对象。
    public object UserSuppliedState { get; }
 
    // 在各种应用程序模型适合的线程或上下文中调用委托。
    public void Post(SendOrPostCallback d, object arg);
    // 结束异步操作的生存期。
    public void OperationCompleted();
    // 效果同调用 Post() + OperationCompleted() 方法组合
    public void PostOperationCompleted(SendOrPostCallback d, object arg);
}

    先分析下这两个帮助器类:

1.   AsyncOperationManager是静态类。静态类是密封的,因此不可被继承。倘若从静态类继承会报错“静态类必须从 Object 派生”。(小常识,以前以为密封类就是 sealed 关键字

2.   AsyncOperationManager为支持异步方法调用的类提供并发管理,该类可正常运行于 .NET Framework 支持的所有应用程序模式下。

3.   AsyncOperation实例提供对特定异步任务的生存期进行跟踪。可用来处理任务完成通知,还可用于在不终止异步操作的情况下发布进度报告和增量结果(这种不终止异步操作的处理是通过AsyncOperation的 Post() 方法实现)

4.   AsyncOperation类有一个私有的构造函数和一个内部CreateOperation() 静态方法。由AsyncOperationManager类调用AsyncOperation.CreateOperation() 静态方法来创建AsyncOperation实例。

5.   AsyncOperation类是通过SynchronizationContext类来实现在各种应用程序的适当“线程或上下文”调用客户端事件处理程序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 提供在各种同步模型中传播同步上下文的基本功能。
public class SynchronizationContext
{
    // 获取当前线程的同步上下文。
    public static SynchronizationContext Current { get; }
 
    // 当在派生类中重写时,响应操作已开始的通知。
    public virtual void OperationStarted();
    // 当在派生类中重写时,将异步消息调度到一个同步上下文。
    public virtual void Post(SendOrPostCallback d, object state);
    // 当在派生类中重写时,响应操作已完成的通知。
    public virtual void OperationCompleted();
    ……
}

a)   在AsyncOperation构造函数中调用SynchronizationContext的OperationStarted() ;

b)       在AsyncOperation的 Post() 方法中调用SynchronizationContext的Post() ;

c)   在AsyncOperation的OperationCompleted()方法中调用SynchronizationContext的OperationCompleted();

6.   SendOrPostCallback委托签名:

    // 表示在消息即将被调度到同步上下文时要调用的方法。

public delegate void SendOrPostCallback(object state);

   

基于事件的异步模式的特征

1.   基于事件的异步模式可以采用多种形式,具体取决于某个特定类支持操作的复杂程度:

1)   最简单的类可能只有一个 ***Async方法和一个对应的 ***Completed 事件,以及这些方法的同步版本。

2)   复杂的类可能有若干个 ***Async方法,每种方法都有一个对应的 ***Completed 事件,以及这些方法的同步版本。

3)   更复杂的类还可能为每个异步方法支持取消(CancelAsync()方法)、进度报告和增量结果(ReportProgress() 方法+ProgressChanged事件)。

4)   如果您的类支持多个异步方法,每个异步方法返回不同类型的数据,您应该:

a)   将您的增量结果报告与您的进度报告分开。

b)   使用适当的EventArgs为每个异步方法定义一个单独的 ***ProgressChanged事件以处理该方法的增量结果数据。

5)   如果类不支持多个并发调用,请考虑公开IsBusy属性。

6)   如要异步操作的同步版本中有 Out 和 Ref 参数,它们应做为对应 ***CompletedEventArgs的一部分,eg:

1
2
3
4
5
6
7
8
9
public int MethodName(string arg1, ref string arg2, out string arg3);
 
public void MethodNameAsync(string arg1, string arg2);
public class MethodNameCompletedEventArgs : AsyncCompletedEventArgs
{
    public int Result { get; };
    public string Arg2 { get; };
    public string Arg3 { get; };
}

2.   如果你的组件要支持多个异步耗时的任务并行执行。那么:

1)   为***Async方法多添加一个userState对象参数(此参数应当始终是***Async方法签名中的最后一个参数),用于跟踪各个操作的生存期。

2)   注意要在你构建的异步类中维护一个userState对象的集合。使用 lock 区域保护此集合,因为各种调用都会在此集合中添加和移除userState对象。

3)   在***Async方法开始时调用AsyncOperationManager.CreateOperation并传入userState对象,为每个异步任务创建AsyncOperation对象,userState存储在AsyncOperation的UserSuppliedState属性中。在构建的异步类中使用该属性标识取消的操作,并传递给CompletedEventArgs和ProgressChangedEventArgs参数的UserState属性来标识当前引发进度或完成事件的特定异步任务

4)   当对应于此userState对象的任务引发完成事件时,你构建的异步类应将AsyncCompletedEventArgs.UserState对象从集合中删除。

3.   异常处理

EAP的错误处理和系统的其余部分不一致。首先,异常不会抛出。在你的事件处理方法中,必须查询AsyncCompletedEventArgs的Exception属性,看它是不是null。如果不是null,就必须使用if语句判断Exception派生对象的类型,而不是使用catch块。

另外,如果你的代码忽略错误,那么不会发生未处理的异常,错误会变得未被检测到,应用程序将继续运行,其结果不可预知。

4.   注意:

1)   确保 ***EventArgs类特定于***方法。即当使用 ***EventArgs类时,切勿要求开发人员强制转换类型值。

2)   确保始终引发方法名称Completed 事件。成功完成、异常或者取消时应引发此事件。任何情况下,应用程序都不应遇到这样的情况:应用程序保持空闲状态,而操作却一直不能完成。

3)   确保可以捕获异步操作中发生的任何异常并将捕获的异常指派给 Error 属性。

4)   确保 ***CompletedEventArgs 类将其成员公开为只读属性而不是字段,因为字段会阻止数据绑定。eg:public MyReturnType Result { get; }

5)   在构建 ***CompletedEventArgs 类属性时,通过this.RaiseExceptionIfNecessary() 方法确保属性值被正确使用。Eg:

1
2
3
4
5
6
7
8
9
private bool isPrimeValue;
public bool IsPrime
{
    get
    {
        RaiseExceptionIfNecessary();
        return isPrimeValue;
    }
}

所以,在***Completed事件处理程序中,应当总是先检查 ***CompletedEventArgs.Error 和 ***CompletedEventArgs.Cancelled 属性,然后再访问RunWorkerCompletedEventArgs.Result属性。

 

BackgroundWorker组件

    System.ComponentModel命名空间的BackgroundWorker组件为我们提供了一个简单的多线程应用解决方案,它允许你在单独的线程上运行耗时操作而不会导致用户界面的阻塞。但是,要注意它同一时刻只能运行一个异步耗时操作(使用IsBusy属性判定),并且不能跨AppDomain边界进行封送处理(不能在多个AppDomain中执行多线程操作)。

1.   BackgroundWorker组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class BackgroundWorker : Component
{
    public BackgroundWorker();
 
    // 获取一个值,指示应用程序是否已请求取消后台操作。
    public bool CancellationPending { get; }
    // 获取一个值,指示BackgroundWorker是否正在运行异步操作。
    public bool IsBusy { get; }
    // 获取或设置一个值,该值指示BackgroundWorker能否报告进度更新。
    public bool WorkerReportsProgress { get; set; }
    // 获取或设置一个值,该值指示BackgroundWorker是否支持异步取消。
    public bool WorkerSupportsCancellation { get; set; }
 
    // 调用RunWorkerAsync() 时发生。
    public event DoWorkEventHandlerDoWork;
    // 调用ReportProgress(System.Int32) 时发生。
    public event ProgressChangedEventHandlerProgressChanged;
    // 当后台操作已完成、被取消或引发异常时发生。
    public event RunWorkerCompletedEventHandlerRunWorkerCompleted;
 
    // 请求取消挂起的后台操作。
    public void CancelAsync();
    // 引发ProgressChanged事件。percentProgress:范围从 0% 到 100%
    public void ReportProgress(int percentProgress);
    // userState:传递到RunWorkerAsync(System.Object) 的状态对象。
    public void ReportProgress(int percentProgress, object userState);
    // 开始执行后台操作。
    public void RunWorkerAsync();
    // 开始执行后台操作。argument:传递给DoWork事件的DoWorkEventArgs参数。
    public void RunWorkerAsync(object argument);
}

2.   相应的EventArgs类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
///1)   System.EventArgs基类
    // System.EventArgs是包含事件数据的类的基类。
    public class EventArgs
    {
        // 表示没有事件数据的事件。
        public static readonly EventArgs Empty;
        public EventArgs();
    }
 
///2)   DoWorkEventArgs类
    // 为可取消的事件提供数据。
    public class CancelEventArgs : EventArgs
    {
        public CancelEventArgs();
        public CancelEventArgs(bool cancel);
        // 获取或设置指示是否应取消事件的值。
        public bool Cancel { get; set; }
    }
    // 为DoWork事件处理程序提供数据。
    public class DoWorkEventArgs : CancelEventArgs
    {
        public DoWorkEventArgs(object argument);
 
        // 获取表示异步操作参数的值。
        public object Argument { get; }
        // 获取或设置表示异步操作结果的值。
        public object Result { get; set; }
    }
 
///3)   ProgressChangedEventArgs类
    // 为ProgressChanged事件提供数据。
    public class ProgressChangedEventArgs : EventArgs
    {
        public ProgressChangedEventArgs(int progressPercentage, object userState);
 
        // 获取异步任务的进度百分比。
        public int ProgressPercentage { get; }
        // 获取唯一的用户状态。
        public object UserState { get; }
    }
 
///4)   RunWorkerCompletedEventArgs类
    // 为MethodNameCompleted事件提供数据。
    public class AsyncCompletedEventArgs : EventArgs
    {
        public AsyncCompletedEventArgs();
        public AsyncCompletedEventArgs(Exception error, bool cancelled, object userState);
 
        // 获取一个值,该值指示异步操作是否已被取消。
        public bool Cancelled { get; }
        // 获取一个值,该值指示异步操作期间发生的错误。
        public Exception Error { get; }
        // 获取异步任务的唯一标识符。
        public object UserState { get; }
 
        // 访问 AsyncCompletedEventArgs 及其派生类的属性前调用此方法
        protected void RaiseExceptionIfNecessary()
        {
            if (this.Error != null)
            {
                throw new TargetInvocationException(……);
            }
            if (this.Cancelled)
            {
                throw new InvalidOperationException(……);
            }
        }
    }
    public class RunWorkerCompletedEventArgs : AsyncCompletedEventArgs
    {
        public RunWorkerCompletedEventArgs(object result, Exception error, bool cancelled);
 
        // 获取表示异步操作结果的值。
        public object Result { get; }
        // 获取表示用户状态的值。
        public object UserState { get; }
    }

3.   BackgroundWorker示例

示例代码中包含了BackgroundWorker源代码及对应的使用示例,这里不粘贴代码了,会导致篇幅更大。来个示例截图吧:

image

 

示例分析:

1)   首先我们为BackgroundWorker组件注册DoWork(异步操作)、ProgressChanged(进度报告) 和RunWorkCompleted(完成通知)事件;

2)   设置WorkerSupportsCancellation和WorkerReportsProgress属性为true,以声明组件支持取消操作和进度报告;

3)   使用RunWorkerAsync() 开启异步操作,通过IsBusy属性判断是否已经有异步任务在执行;

4)   使用CancelAsync() 方法取消异步操作,但要注意:

a)   它仅仅是将BackgroudWorker.CancellationPending属性设置为true。需要在具体DoWork事件中不断检查BackgroudWorker.CancellationPending来设置DoWorkEventArgs的Cancel属性。

b)   DoWork事件处理程序中的代码有可能在发出取消请求时完成其工作,轮询循环可能会错过设置为 true 的CancellationPending属性。在这种情况下,即使发出了取消请求,RunWorkerCompleted事件处理程序中RunWorkerCompletedEventArgs的 Cancelled 标志也不会设置为 true。这种情况被称作争用状态。(可以通过直接监控组件的CancellationPending属性,来做判断)

5)   确保在DoWork事件处理程序中不操作任何用户界面对象。而应该通过ProgressChanged和RunWorkerCompleted事件与用户界面进行通信。

因为RunWorkerAsync() 是通过委托的BeginInvoke() 引发的DoWork事件,即DoWork事件的执行线程已不是创建控件的线程(我在《异步编程:异步编程模型 (APM)》中介绍了几种夸线程访问控件的方式)。而ProgressChanged和RunWorkerCompleted事件是通过帮助器类AsyncOperation的 Post() 方法使其调用发生在合适的“线程或上下文”中。

 

自定义基于事件的异步组件

    刚才我们介绍了BackgroundWorker组件,但是这个组件在一个时刻只能开启一个异步操作,那如果我们要想同时支持多个异步操作、进度报告、增量结果、取消和返回结果值或异常信息该怎么办呢?对的,我们可以为自己定义一个基于事件的异步组件。

    我直接引用MSDN上的一则计算质数的异步组件示例,请从我提供的示例代码中获取

质数算法:埃拉托色尼筛法

eg:判断n是否为质数

1、1和0既非素数也非合数;

2、将2和3加入质数集合primes;从n=5开始,通过 n+=2 来跳过所有偶数;

3、循环集合primes中的质数并将其做为n的因子,能整除的为合数;

4、若不能整除,则继续循步骤3直到“因子的平方>n”,即可判断n为质数,并将其加入到集合primes。

 

来个示例截图吧:

image

 

示例分析:(组件名:PrimeNumberCalculator)

1.   首先我们为PrimeNumberCalculator组件注册ProgressChanged(进度报告) 和CalculatePrimeCompleted(完成通知)事件;

2.   使用CalculatePrimeAsync(intnumberToTest, object taskId)开启异步任务,注意我们需要传递一个唯一标识Guid taskId = Guid.NewGuid();用于标识取消的操作,并传递给CompletedEventArgs和ProgressChangedEventArgs参数的UserState属性来标识当前引发进度或完成事件的特定异步任务;

3.   取消操作CancelAsync(object taskId),只是将taskId对应的AsyncOperation实例移除内部任务集合,耗时操作通过判断taskId是否存在于集合来判断其是否被取消;

 

 

此文到此结束,通过此博文我们认识到:

1)   基于事件的异步编程是通过AsyncOperationManager类和AsyncOperation类两个帮助器类确保你的异步操作支持在各种应用程序模型(包括 ASP.NET、控制台应用程序和 Windows 窗体应用程序)的适当“线程或上下文”调用访问控件;

2)   BackgroundWorker组件构建、使用和缺点。

3)   展现如何构建一个基于事件的异步组件,并且支持多个异步操作的并行运行

 

感谢大家的观赏,如本文对你有帮助还请多帮推荐支持下……

 

 

 

参考:MSDN

             书籍:《CLR via C#(第三版)》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/437308.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【转】1.8异步编程:.NET 4.5 基于任务的异步编程模型(TAP)

传送门:异步编程系列目录…… 最近我为大家陆续介绍了“IAsyncResult异步编程模型 (APM)”和“基于事件的异步编程模式(EAP)”两种异步编程模型。在.NET4.0 中Microsoft又为我们引入了新的异步编程模型“基于任务的异步编程模型(TAP)”,并且推荐我们在开…

Qt: 找不到Qt5Widgets.lib

在静态编译的时候,提示错误: error: dependent ‘D:\IDE\Qt\5.4.2-mingw32-rel-static\5.4.2-mingw32-rel-static\lib\Qt5Widgets.lib 去目录下看了下,全部是libxxxxx.a文件,是linux的库文件。但是之前编译是正常的,这…

python3读写excel文件_python3 循环读取excel文件并写入json操作

文件内容:excel内容:代码: import xlrd import json import operator def read_xlsx(filename): # 打开excel文件 data1 xlrd.open_workbook(filename) # 读取第一个工作表 table data1.sheets()[0] # 统计行数 n_rows table.nrows data …

Qt:error LNK2038: 检测到“_MSC_VER”的不匹配项: 值“1600”不匹配值“1800

Visual Studio 2013生成Qt项目时报错。网上搜说是更改平台工具集,试了没用。退一步说我就是需要使用vs2013,改成其他的会不符合项目需求。于是打开了项目文件.sln,如下: 才发现目标工程的Qt版本是5.7.0,vs2013里面设置…

JAVA程序绑定到指定的CPU核上

由于服务器上某几个核被C程序绑定了&#xff0c;我们的java程序有的线程会使用到&#xff0c;导致C程序丢包异常&#xff0c;所以需要将JAVA程序绑定到指定的CPU核上 1.命令介绍 1.taskset命令 taskset -c <cpu核编号> <pid> #可以指定进程绑定到哪个cpu核上2.t…

【转】URN_URI_URL详解

URI&#xff0c;Uniform Resource Identifier&#xff0c;统一资源标识符。 URN&#xff0c;Uniform Resource Name&#xff0c;统一资源命名 URL&#xff0c;Uniform Resource Location&#xff0c;统一资源定位符。 URI 简单来理解就是标识/定义了一个资源&#xff0c;而 URL…

彻底弄懂Qt的编码(汉字乱码问题及相关函数作用)

测试1 新建test工程用于测试&#xff0c;main.c文件内容如下&#xff1a; #include <QCoreApplication> #include <QDebug>int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);QString str_hanzi("百度"); // 汉字QString str_ascii(&…

【转】1.9 Asp.Net Core 轻松学-多线程之取消令牌(

目录 前言1. 多线程请求合并数据源2. 对长时间阻塞调用的异步取消令牌应用3. CancellationToken 的链式反应4. CancellationToken 令牌取消的三种方式结束语示例代码下载前言 取消令牌(CancellationToken) 是 .Net Core 中的一项重要功能&#xff0c;正确并合理的使用 Cancell…

python怎么改背景_python IDE背景怎么改

首先&#xff0c;在已经下载好的python文件目录下&#xff0c;找到config-highlight.def文件&#xff0c;我的是在H:\python\python3**\Lib\idlelib**文件夹下。 打开文件后&#xff0c;你会看到一些默认的颜色配置&#xff0c;比如经典的颜色配置就是白色背景&#xff0c;一般…

QML程序发布时无法正常运行的解决办法

1、运行依赖 以我的一个项目为例&#xff0c;此程序使用QQuickWidget将QWidget和QML结合。程序debug版发布时依赖的库如下&#xff1a; 大部分dll可以在Visual Studio中调试时的控制台中看出已加载的dll&#xff0c;只需到Qt安装目录下找到对应的dll即可。但是某些dll并没有提…

【转】UML基础: 第1部分 - 类图 (Class Diagram)

类图 类图是一个静态图。它代表了应用程序的静态视图。类图不仅用于可视化&#xff0c;描述和记录系统的不同方面&#xff0c;还用于构建软件应用程序的可执行代码。 类图描述了一个类的属性和操作&#xff0c;以及对系统施加的约束。类图被广泛用于面向对象系统的建模&#…

int转换为cstring_PostgreSQL 隐式类型转换探秘

个人简介何小栋&#xff0c; 从事产品研发和架构设计工作&#xff0c;对Oracle、PostgreSQL有深入研究&#xff0c;ITPUB数据库版块资深版主。现就职于广州云图数据技术有限公司&#xff0c;系统架构师&#xff0c;博客&#xff1a;http://blog.itpub.net/6906/摘要本文通过与O…

【转】UML基础: 第 2 部分 - 对象图 (Object Diagram)

对象图是从类图派生的&#xff0c;因此对象图依赖于类图。 对象图表示类图的一个实例。类图和对象图的基本概念是相似的。对象图也表示系统的静态视图&#xff0c;但这个静态视图是系统在特定时刻的快照。 对象图用于呈现一组对象及其关系作为实例。 对象图的目的 图表的目…

外部依赖项很多未定义标识符_从日本编程书籍《我的第一本编程书》中译版看中文例程如何扬长避短——标识符(一)

日本作者平山尚在前言归结了本书的三点独特之处&#xff1a;从始至终只编写一个程序&#xff08;俄罗斯方块游戏&#xff09;使用专门的工具绝对面向首次接触程序的人群第一点&#xff0c;优势是一个项目主体贯穿全书&#xff0c;但同时很考验编排顺序&#xff0c;以及技术覆盖…

Qt: QTableView如何获取(行)选中、行切换信息

**情景&#xff1a;**做一个信息表格&#xff0c;需要多个Model切换&#xff0c;必须用QTableView&#xff0c;而不能用QTableWidget&#xff0c;因为后者不可以进行setModel()。 方案&#xff1a; QTableView和选择有关的的信号有&#xff1a; void activated(const QModelI…

动态网站的技术路线_3个好玩实用小网站!闲暇时间不妨打开看看

感谢你关注“最佳应用”每篇文章解决某行业或某人群的一个痛点第八十四期原创文章By&#xff1a;小佳昨天刷抖音听了一首很有魔性的歌曲&#xff0c;结果分享到社交平台&#xff0c;没想到被很多键盘侠喷了&#xff0c;留言全是批判“审美有毒”&#xff0c;这种垃圾歌曲能火就…

【转】1.DThread、ThreadPool、Task、Parallel的基本用法、区别以及弊端

多线程的操作在程序中也是比较常见的&#xff0c;比如开启一个线程执行一些比较耗时的操作(IO操作)&#xff0c;而主线程继续执行当前操作&#xff0c;不会造成主线程阻塞。线程又分为前台线程和后台线程&#xff0c;区别是&#xff1a;整个程序必须要运行完前台线程才会退出&a…

vue传值到后端_Vue.js快速入门就从这儿开始特别是后端程序员

自从前后端分离开始变成主流后&#xff0c;曾经的Jsp、FreeMarker、Velocity、Thymeleaf貌似慢慢被遗忘了&#xff0c;取而代之的是兴起的前端主流语言&#xff0c;比如Vue、React和AngularJS介绍VueVue其实是借鉴了 Angular&#xff0c;目前GitHubstar数最多&#xff0c;建议后…

unity3d collider自动调整大小_自动网格组合建模工具Unity游戏素材资源

分享最新的CG教程与素材资讯&#xff01;人人素材RRCG-专业的CG艺术交流网站点击上方蓝字关注人人素材本游戏资料是自动网格组合建模工具Unity游戏素材资源&#xff0c;大小&#xff1a;735 KB &#xff0c;格式&#xff1a;unitypackage&#xff0c;使用软件&#xff1a;unity…

【转】2.2[译]async/await中阻塞死锁

这篇博文主要是讲解在async/await中使用阻塞式代码导致死锁的问题&#xff0c;以及如何避免出现这种死锁。内容主要是从作者Stephen Cleary的两篇博文中翻译过来. 原文1&#xff1a;DontBlock on Async Code 原文2&#xff1a;why the AspNetSynchronizationContext was remove…