【转】1.6异步编程:IAsyncResult异步编程模型 (APM)

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

         大部分开发人员,在开发多线程应用程序时,都是使用ThreadPool的QueueUserWorkItem方法来发起一次简单的异步操作。然而,这个技术存在许多限制。最大的问题是没有一个内建的机制让你知道操作在什么时候完成,也没有一个机制在操作完成时获得一个返回值。为了克服这些限制(并解决其他一些问题),Microsoft引入了三种异步编程模式:

1.         .NET1.0异步编程模型 (APM),基于IAsyncResult接口实现。

2.         .NET2.0基于事件的异步编程模式(EAP),基于事件实现。

3.         .NET4.X基于任务的异步编程模式(TPL->TAP),新型异步编程模式,对于.NET4.0之后的异步构造都推荐使用此模式

尽管在新的设计上我们推荐都使用“.NET4.0基于任务的编程模式”,但我还是计划整理出旧版的异步编程模型,因为:

1.         在一些特殊场合下我们可能觉得一种模式更适合;

2.         可以更充分认识三种模式之间的优劣,便于选择;

3.         很多遗留的代码包含了旧的设计模式;

4.         等等…

 

 

示例下载:异步编程:IAsyncResult异步编程模型.rar

 

IAsyncResult设计模式----规范概述

使用IAsyncResult设计模式的异步操作是通过名为 Begin*** 和 End*** 的两个方法来实现的,这两个方法分别指代开始和结束异步操作。例如,FileStream类提供BeginRead和EndRead方法来从文件异步读取字节。这两个方法实现了 Read 方法的异步版本。

在调用 Begin*** 后,应用程序可以继续在调用线程上执行指令,同时异步操作在另一个线程上执行。(如果有返回值还应调用 End*** 来获取操作的结果)。

1)         Begin***

a)         Begin*** 方法带有该方法的同步版本签名中声明的任何参数。

b)         Begin*** 方法签名中不包含任何输出参数。方法签名最后两个参数的规范是:第一个参数定义一个AsyncCallback委托,此委托引用在异步操作完成时调用的方法。第二个参数是一个用户定义的对象。此对象可用来向异步操作完成时为AsyncCallback委托方法传递应用程序特定的状态信息(eg:可通过此对象在委托中访问End*** 方法)。另外,这两个参数都可以传递null

c)         返回IAsyncResult对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 表示异步操作的状态。
[ComVisible(true)]
public interface IAsyncResult
{
    // 获取用户定义的对象,它限定或包含关于异步操作的信息。
    object AsyncState { get; }
    // 获取用于等待异步操作完成的System.Threading.WaitHandle,待异步操作完成时获得信号。
    WaitHandle AsyncWaitHandle { get; }
    // 获取一个值,该值指示异步操作是否同步完成。
    bool CompletedSynchronously { get; }
    // 获取一个值,该值指示异步操作是否已完成。
    bool IsCompleted { get; }
}
 
// 常用委托声明(我后面示例是使用了自定义的带ref参数的委托)
public delegate void AsyncCallback(IAsyncResult ar)

2)         End***

a)         End*** 方法可结束异步操作,如果调用 End*** 时,IAsyncResult对象表示的异步操作还未完成,则 End*** 将在异步操作完成之前阻塞调用线程。

b)         End*** 方法的返回值与其同步副本的返回值类型相同。End*** 方法带有该方法同步版本的签名中声明的所有out 和 ref 参数以及由BeginInvoke返回的IAsyncResult,规范上 IAsyncResult 参数放最后。

                         i.              要想获得返回结果,必须调用的方法;

                       ii.              若带有out 和 ref 参数,实现上委托也要带有out 和 ref 参数,以便在回调中获得对应引用传参值做相应逻辑;

3)         总是调用 End***() 方法,而且只调用一次

以下理由都是针对“I/O限制”的异步操作提出。然而,对于计算限制的异步操作,尽管都是用户代码,但还是推荐遵守此规则。

I/O限制的异步操作:比如像带FileOptions.Asynchronous标识的FileStream,其BeginRead()方法向Windows发送一个I/O请求包(I/O Request Packet,IRP)后方法不会阻塞线程而是立即返回,由Windows将IRP传送给适当的设备驱动程序,IRP中包含了为BeginRead()方法传入的回调函数,待硬件设备处理好IRP后,会将IRP的委托排队到CLR的线程池队列中。

必须调用End***方法,否则会造成资源的泄露。有的开发人员写代码调用Begin***方法异步执行I/O限制后就不需要进行任何处理了,所以他们不关心End***方法的调用。但是,出于以下两个原因,End***方法是必须调用的:

a)         在异步操作时,对于I/O限制操作,CLR会分配一些内部资源,操作完成时,CLR继续保留这些资源直至End***方法被调用。如果一直不调用End***,这些资源会直到进程终止时才会被回收。(End***方法设计中常常包含资源释放)

b)         发起一个异步操作时,实际上并不知道该操作最终是成功还是失败(因为操作由硬件在执行)。要知道这一点,只能通过调用End***方法,检查它的返回值或者看它是否抛出异常。

另外,需要注意的是I/O限制的异步操作完全不支持取消(因为操作由硬件执行),但可以设置一个标识,在完成时丢弃结果来模拟取消行为。

 

 

 

现在我们清楚了IAsyncResult设计模式的设计规范,接下来我们再通过IAsyncResult异步编程模式的三个经典场合来加深理解。

 

一、基于IAsyncResult构造一个异步API

         现在来构建一个IAsyncResult的类,并且实现异步调用。

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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
// 带ref参数的自定义委托
public delegate void RefAsyncCallback(ref string resultStr, IAsyncResult ar);
 
public class CalculateAsyncResult : IAsyncResult
{
    private int _calcNum1;
    private int _calcNum2;
    private RefAsyncCallback _userCallback;
 
    public CalculateAsyncResult(int num1, int num2, RefAsyncCallback userCallback, object asyncState)
    {
        this._calcNum1 = num1;
        this._calcNum2 = num2;
        this._userCallback = userCallback;
        this._asyncState = asyncState;
        // 异步执行操作
        ThreadPool.QueueUserWorkItem((obj) => { AsyncCalculate(obj); }, this);
    }
 
    #region IAsyncResult接口
    private object _asyncState;
    public object AsyncState { get { return _asyncState; } }
 
    private ManualResetEvent _asyncWaitHandle;
    public WaitHandle AsyncWaitHandle
    {
        get
        {
            if (this._asyncWaitHandle == null)
            {
                ManualResetEvent event2 = new ManualResetEvent(false);
                Interlocked.CompareExchange<ManualResetEvent>(ref this._asyncWaitHandle, event2, null);
            }
            return _asyncWaitHandle;
        }
    }
 
    private bool _completedSynchronously;
    public bool CompletedSynchronously { get { return _completedSynchronously; } }
 
    private bool _isCompleted;
    public bool IsCompleted { get { return _isCompleted; } }
    #endregion
 
    /// <summary>
    ///
    /// 存储最后结果值
    /// </summary>
    public int FinnalyResult { get; set; }
    /// <summary>
    /// End方法只应调用一次,超过一次报错
    /// </summary>
    public int EndCallCount = 0;
    /// <summary>
    /// ref参数
    /// </summary>
    public string ResultStr;
 
    /// <summary>
    /// 异步进行耗时计算
    /// </summary>
    /// <param name="obj">CalculateAsyncResult实例本身</param>
    private static void AsyncCalculate(object obj)
    {
        CalculateAsyncResult asyncResult = obj as CalculateAsyncResult;
        Thread.SpinWait(1000);
        asyncResult.FinnalyResult = asyncResult._calcNum1 * asyncResult._calcNum2;
        asyncResult.ResultStr = asyncResult.FinnalyResult.ToString();
 
        // 是否同步完成
        asyncResult._completedSynchronously = false;
        asyncResult._isCompleted = true;
        ((ManualResetEvent)asyncResult.AsyncWaitHandle).Set();
        if (asyncResult._userCallback != null)
            asyncResult._userCallback(ref asyncResult.ResultStr, asyncResult);
    }
}
 
public class CalculateLib
{
    public IAsyncResult BeginCalculate(int num1, int num2, RefAsyncCallback userCallback, object asyncState)
    {
        CalculateAsyncResult result = new CalculateAsyncResult(num1, num2, userCallback, asyncState);
        return result;
    }
 
    public int EndCalculate(ref string resultStr, IAsyncResult ar)
    {
        CalculateAsyncResult result = ar as CalculateAsyncResult;
        if (Interlocked.CompareExchange(ref result.EndCallCount, 1, 0) == 1)
        {
            throw new Exception("End方法只能调用一次。");
        }
        result.AsyncWaitHandle.WaitOne();
 
        resultStr = result.ResultStr;
 
        return result.FinnalyResult;
    }
 
    public int Calculate(int num1, int num2, ref string resultStr)
    {
        resultStr = (num1 * num2).ToString();
        return num1 * num2;
    }
}

         使用上面通过IAsyncResult设计模式实现的带ref引用参数的异步操作,我将展示三种阻塞式响应异步调用和一种无阻塞式委托响应异步调用。即:

1.         执行异步调用后,若我们需要控制后续执行代码在异步操作执行完之后执行,可通过下面三种方式阻止其他工作:(当然我们不推荐你阻塞线程或轮询浪费CPU时间)

a)         IAsyncResult的AsyncWaitHandle属性,待异步操作完成时获得信号。

b)         通过IAsyncResult的IsCompleted属性进行轮询。

c)         调用异步操作的 End*** 方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/// <summary>
/// APM 阻塞式异步响应
/// </summary>
public class Calculate_For_Break
{
    public static void Test()
    {
        CalculateLib cal = new CalculateLib();
 
        // 基于IAsyncResult构造一个异步API   (回调参数和状态对象都传递null)
        IAsyncResult calculateResult = cal.BeginCalculate(123, 456, null, null);
        // 执行异步调用后,若我们需要控制后续执行代码在异步操作执行完之后执行,可通过下面三种方式阻止其他工作:
        // 1、IAsyncResult 的 AsyncWaitHandle 属性,带异步操作完成时获得信号。
        // 2、通过 IAsyncResult 的 IsCompleted 属性进行轮询。通过轮询还可实现进度条功能。
        // 3、调用异步操作的 End*** 方法。
        // ***********************************************************
        // 1、calculateResult.AsyncWaitHandle.WaitOne();
        // 2、while (calculateResult.IsCompleted) { Thread.Sleep(1000); }
        // 3、
        string resultStr = string.Empty;
        int result = cal.EndCalculate(ref resultStr, calculateResult);
    }
}

2.         执行异步调用后,若我们不需要阻止后续代码的执行,那么我们可以把异步执行操作后的响应放到回调中进行。(推荐使用无阻塞式回调模式)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/// <summary>
/// APM 回调式异步响应
/// </summary>
public class Calculate_For_Callback
{
    public static void Test()
    {
        CalculateLib cal = new CalculateLib();
 
        // 基于IAsyncResult构造一个异步API
        IAsyncResult calculateResult = cal.BeginCalculate(123, 456, AfterCallback, cal);
    }
 
    /// <summary>
    /// 异步操作完成后做出响应
    /// </summary>
    private static void AfterCallback(ref string resultStr, IAsyncResult ar)
    {
        // 执行异步调用后,若我们不需要阻止后续代码的执行,那么我们可以把异步执行操作后的响应放到回调中进行。
        CalculateLib cal = ar.AsyncState as CalculateLib;
        cal.EndCalculate(ref resultStr, ar);
        // 再根据resultStr值做逻辑。
    }
}

 

二、使用委托进行异步编程

对于委托,编译器会为我们生成同步调用方法“invoke”以及异步调用方法“BeginInvoke”和“EndInvoke”。对于异步调用方式,公共语言运行库 (CLR) 将对请求进行排队并立即返回到调用方,由线程池的线程调度目标方法并与提交请求的原始线程并行运行,为BeginInvoke()方法传入的回调方法也将在同一个线程上运行。

异步委托是快速为方法构建异步调用的方式,它基于IAsyncResult设计模式实现的异步调用,即,通过BeginInvoke返回IAsyncResult对象;通过EndInvoke获取结果值。

示例:

上节的CalculateLib类中的同步方法以及所要使用到的委托如下:

1
2
3
4
5
6
7
// 带ref参数的自定义委托
public delegate int AsyncInvokeDel(int num1, int num2, ref string resultStr);
public int Calculate(int num1, int num2, ref string resultStr)
{
    resultStr = (num1 * num2).ToString();
    return num1 * num2;
}

然后,通过委托进行同步或异步调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/// <summary>
/// 使用委托进行异步调用
/// </summary>
public class Calculate_For_Delegate
{
    public static void Test()
    {
        CalculateLib cal = new CalculateLib();
 
        // 使用委托进行同步或异步调用
        AsyncInvokeDel calculateAction = cal.Calculate;
        string resultStrAction = string.Empty;
        // int result1 = calculateAction.Invoke(123, 456);
        IAsyncResult calculateResult1 = calculateAction.BeginInvoke(123, 456, ref resultStrAction, null, null);
        int result1 = calculateAction.EndInvoke(ref resultStrAction, calculateResult1);
    }
}

 

三、多线程操作控件

访问 Windows 窗体控件本质上不是线程安全的。如果有两个或多个线程操作某一控件的状态,则可能会迫使该控件进入一种不一致的状态。还可能出现其他与线程相关的 bug,包括争用情况和死锁。确保以线程安全方式访问控件非常重要。

不过,在有些情况下,您可能需要多线程调用控件的方法。.NET Framework 提供了从任何线程操作控件的方式:

1.         非安全方式访问控件(此方式请永远不要再使用)

多线程访问窗口中的控件,可以在窗口的构造函数中将Form的CheckForIllegalCrossThreadCalls静态属性设置为false。

1
2
3
4
// 获取或设置一个值,该值指示是否捕获对错误线程的调用,
// 这些调用在调试应用程序时访问控件的System.Windows.Forms.Control.Handle属性。
// 如果捕获了对错误线程的调用,则为 true;否则为 false。
public static bool CheckForIllegalCrossThreadCalls { get; set; }

2.         安全方式访问控件

原理:从一个线程封送调用并跨线程边界将其发送到另一个线程,并将调用插入到创建控件线程的消息队列中,当控件创建线程处理这个消息时,就会在自己的上下文中执行传入的方法。(此过程只有调用线程和创建控件线程,并没有创建新线程)

注意:从一个线程封送调用并跨线程边界将其发送到另一个线程会耗费大量的系统资源,所以应避免重复调用其他线程上的控件。

1)         使用BackgroundWork后台辅助线程控件方式(详见:基于事件的异步编程模式(EMP))。

2)         结合TaskScheduler.FromCurrentSynchronizationContext() 和Task 实现。

3)         捕获线程上下文ExecuteContext,并调用ExeceteContext.Run()静态方法在指定的线程上下文中执行。(详见:执行上下文

4)         使用Control类上提供的Invoke 和BeginInvoke方法。

5)         在WPF应用程序中可以通过WPF提供的Dispatcher对象提供的Invoke方法、BeginInvoke方法来完成跨线程工作。

 

因本文主要解说IAsyncResult异步编程模式,所以只详细分析Invoke 和BeginInvoke跨线程访问控件方式。

         Control类实现了ISynchronizeInvoke接口,提供了Invoke和BeginInvoke方法来支持其它线程更新GUI界面控件的机制。

1
2
3
4
5
6
7
8
9
10
public interface ISynchronizeInvoke
{
    // 获取一个值,该值指示调用线程是否与控件的创建线程相同。
    bool InvokeRequired { get; }
    // 在控件创建的线程上异步执行指定委托。
    AsyncResult BeginInvoke(Delegate method, params object[] args);
    object EndInvoke(IAsyncResult asyncResult);
    // 在控件创建的线程上同步执行指定委托。
    object Invoke(Delegate method, params object[] args);
}

1)         Control类的 Invoke,BeginInvoke 内部实现如下:

a)         Invoke                (同步调用)先判断控件创建线程与当前线程是否相同,相同则直接调用委托方法;否则使用Win32API的PostMessage 异步执行,但是 Invoke 内部会调用IAsyncResult.AsyncWaitHandle等待执行完成。

b)         BeginInvoke     (异步调用)使用Win32API的PostMessage 异步执行,并且返回 IAsyncResult 对象。

1
2
3
4
UnsafeNativeMethods.PostMessage(new HandleRef(this, this.Handle)
                  , threadCallbackMessage, IntPtr.Zero, IntPtr.Zero);
[DllImport("user32.dll", CharSet=CharSet.Auto)]
public static extern bool PostMessage(HandleRefhwnd, intmsg, IntPtrwparam, IntPtrlparam);

PostMessage 是windows api,用来把一个消息发送到一个窗口的消息队列。这个方法是异步的,也就是该方法封送完毕后马上返回,不会等待委托方法的执行结束,调用者线程将不会被阻塞。(对应同步方法的windows api是:SendMessage();消息队列里的消息通过调用GetMessage和PeekMessage取得)

2)         InvokeRequired

获取一个值,该值指示调用线程是否与控件的创建线程相同。内部关键如下:

1
2
3
Int windowThreadProcessId = SafeNativeMethods.GetWindowThreadProcessId(ref2, out num);
Int currentThreadId = SafeNativeMethods.GetCurrentThreadId();
return (windowThreadProcessId != currentThreadId);

即返回“通过GetWindowThreadProcessId功能函数得到创建指定窗口线程的标识和创建窗口的进程的标识符与当前线程Id进行比较”的结果。

3)         示例(详见示例文件)

在使用的时候,我们使用 this.InvokeRequired 属性来判断是使用Invoke或BeginInvoke 还是直接调用方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private void InvokeControl(object mainThreadId)
{
    if (this.InvokeRequired)
    {
        this.Invoke(new Action<String>(ChangeText), "InvokeRequired = true.改变控件Text值");
        //this.textBox1.Invoke(new Action<int>(InvokeCount), (int)mainThreadId);
    }
    else
    {
        ChangeText("在创建控件的线程上,改变控件Text值");
    }
}
 
private void ChangeText(String str)
{
    this.textBox1.Text += str;
}

         注意,在InvokeControl方法中使用 this.Invoke(Delegate del) 和使用 this.textBox1.Invoke(Delegate del) 效果是一样的。因为在执行Invoke或BeginInvoke时,内部首先调用 FindMarshalingControl() 进行一个循环向上回溯,从当前控件开始回溯父控件,直到找到最顶级的父控件,用它作为封送对象。也就是说 this.textBox1.Invoke(Delegate del) 会追溯到和 this.Invoke(Delegate del) 一样的起点。(子控件的创建线程一定是创建父控件的线程,所以这种追溯不会导致将调用封送到错误的目的线程)

4)         异常信息:"在创建窗口句柄之前,不能在控件上调用 Invoke 或 BeginInvoke"

a)         可能是在窗体还未构造完成时,在构造函数中异步去调用了Invoke 或BeginInvoke;

b)         可能是使用辅助线程创建一个窗口并用Application.Run()去创建句柄,在句柄未创建好之前调用了Invoke 或BeginInvoke。(此时新建的窗口相当于开了另一个进程,并且为新窗口关联的辅助线程开启了消息循环机制),类似下面代码:

1
2
3
4
5
new Thread((ThreadStart)delegate
    {
        WaitBeforeLogin = new Form2();
        Application.Run(WaitBeforeLogin);
    }).Start();

解决方案:在调用Invoke 或 BeginInvoke之前轮询检查窗口的IsHandleCreated属性。

1
2
3
// 获取一个值,该值指示控件是否有与它关联的句柄。
public bool IsHandleCreated { get; }
while (!this.IsHandleCreated) { …… }

 

 

 

    本节到此结束,本节主要讲了异步编程模式之一“异步编程模型(APM)”,是基于IAsyncResult设计模式实现的异步编程方式,并且构建了一个继承自IAsyncResult接口的示例,及展示了这种模式在委托及跨线程访问控件上的经典应用。下一节中,我将为大家介绍基于事件的编程模型……
    感谢大家的观看,如觉得文章不错还请多多推荐……

 

 

 

参考:MSDN

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

 


作者:滴答的雨
出处:http://www.cnblogs.com/heyuquan/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

   
 

“1. 执行异步调用后,若我们需要控制后续执行代码在异步操作执行完之后执行,可通过下面三种方式阻止其他工作:(当然我们不推荐你阻塞线程或轮询浪费CPU时间)

a) IAsyncResult的AsyncWaitHandle属性,待异步操作完成时获得信号。

b) 通过IAsyncResult的IsCompleted属性进行轮询。

c) 调用异步操作的 End*** 方法。

2. 执行异步调用后,若我们不需要阻止后续代码的执行,那么我们可以把异步执行操作后的响应放到回调中进行。


你说的a、b、c方式恰好都是不推荐的调用方式,因为会阻塞到线程,并且还给人“在xxx之后完成必须要通过阻塞线程这种方式”的错误印象。恰好推荐的最常用的回调方式你又没说清楚,所以我认为你没懂。而且IAsnycResult实现的还比较繁琐,没有错误处理,并且会有资源泄露可能。而这几点正是CLR VIA C#所提及的。

另外,你要不就
1.阻塞线程,在主线程里调用End(不推荐)
IAsyncResult ar = cal.BeginCalculate(123, 456, null, null);

string resultStr = string.Empty;
int result = cal.EndCalculate(ref resultStr, ar);
//把写在call back的代码放在这里

2.要不就采用回调的方式(推荐)。

而不是像你的代码里这种既有回调,又阻塞线程的混合的使用方式,看起来就像没太懂的人写的代码。
IAsyncResult calculateResult = cal.BeginCalculate(123, 456, AfterCallback, cal);
string resultStr = string.Empty;
int result = cal.EndCalculate(ref resultStr, calculateResult);


关于APM我的看法可以看我的博客,关于IAsyncResult那一节的表述。

 
   
支持(0) 反对(0)
  
 

@ 滴答的雨
IAsyncResult本身就带这几种模式给人选择的灵活性,并不意味着需要一起用。一起用反而让人看起来不但困扰,而且反而是错误用法。

这就是为什一定需要分开写,就算是像我一样偷懒也可以不写,但是就是不能混在一起写。

简单来说,你的代码中,
1.看起来展示的是直接调用的end的这种阻塞的方式。
2.同时定义了call back方法给人以困扰,让人以为这又是个回调方法。
3.因为直接调用end的方式根本不需要通过asyncState来辅助传递参数,避免转型的开销和可能产生的错误。

结果是你既没正确的展示End的用法,又把最常用的回调方法给注释掉了。
就算你多加一个例子,也不过多三五行的代码而已。所以就觉得你没懂。

CalculateLib cal = new CalculateLib();

展示End
IAsyncResult ar = cal.BeginCalculate(123, 456, null, null);
string resultStr = string.Empty;
int result = cal.EndCalculate(ref resultStr, ar);



展示call back
IAsyncResult calculateResult = cal.BeginCalculate(123, 456, AfterCallback, cal);

private static void AfterCallback(ref string resultStr, IAsyncResult ar)
{
CalculateLib cal = ar.AsyncState as CalculateLib;

int result = cal.EndCalculate(ref resultStr, calculateResult1);
if (result > 0) { }
}

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

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

相关文章

Spark worker内存不足导致任务失败,报错Likely due to containers exceeding thresholds, or network issues

报错: Lost executor 33 on xx.xx.xx.152: Remote RPC client disassociated. Likely due to containers exceeding thresholds, or network issues. Check driver logs for WARN messages. 原因&#xff1a; 由于spark某节点可用内存不足导致整个任务失败&#xff0c;在执行…

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

传送门&#xff1a;异步编程系列目录…… 上一篇&#xff0c;我给大家介绍了“.NET1.0 IAsyncResult异步编程模型(APM)”&#xff0c;通过Begin*** 开启操作并返回IAsyncResult对象&#xff0c;使用 End*** 方法来结束操作&#xff0c;通过回调方法来做异步操作后其它事项。然…

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

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

Qt: 找不到Qt5Widgets.lib

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

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

文件内容&#xff1a;excel内容&#xff1a;代码&#xff1a; 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项目时报错。网上搜说是更改平台工具集&#xff0c;试了没用。退一步说我就是需要使用vs2013&#xff0c;改成其他的会不符合项目需求。于是打开了项目文件.sln&#xff0c;如下&#xff1a; 才发现目标工程的Qt版本是5.7.0&#xff0c;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;建议后…