C#的变迁史08 - C# 5.0 之并行编程总结篇

C# 5.0 搭载于.NET 4.5和VS2012之上。

  同步操作既简单又方便,我们平时都用它。但是对于某些情况,使用同步代码会严重影响程序的可响应性,通常来说就是影响程序性能。这些情况下,我们通常是采用异步编程来完成功能,这在前面也多次提及了。异步编程的核心原理也就是使用多线程/线程池和委托来完成任务的异步执行和返回,只不过在每个新的C#版本中,微软都替我们完成了更多的事,使得程序模板越来越傻瓜化了。


  .NET Framework 提供以下两种执行 I/O 绑定和计算绑定异步操作的标准模式:
1. 异步编程模型 (APM,Asynchronous Programming Model)

  在该模型中异步操作由一对 Begin/End 方法(如 FileStream.BeginRead 和 Stream.EndRead)表示。
  异步编程模型是一种模式,该模式使用更少的线程去做更多的事。.NET Framework很多类实现了该模式,这些类都定义了BeginXXX和EndXXX类似的方法,比如FileStream类的BeginRead和EndRead方法。同时我们也可以自定义类来实现该模式(也就是在自定义的类中实现返回类型为IAsyncResult接口的BeginXXX方法和EndXXX方法);另外委托类型也定义了BeginInvoke和EndInvoke方法,使得委托可以异步执行。这些异步操作的背后都是线程池在支撑着,这是微软异步编程的基础架构,也是比较老的模式,不过从中我们可以清楚的了解异步操作的原理。

  所有BeginXXX方法返回的都是实现了IAsyncResult接口的一个对象,并不是对应的同步方法所要得到的结果的。此时我们需要调用对应的EndXXX方法来结束异步操作,并向该方法传递IAsyncResult对象,EndXxx方法的返回类型就是和同步方法一样的。例如,FileStream的EndRead方法返回一个Int32来代表从文件流中实际读取的字节数。

  对于访问异步操作的结果,APM提供了四种方式供开发人员选择:

- 在调用BeginXxx方法的线程上调用EndXXX方法来得到异步操作的结果,但是这种方式会阻塞调用线程,直到操作完成之后调用线程才继续运行
- 查询IAsyncResult的AsyncWaitHandle属性,从而得到WaitHandle,然后再调用它的WaitOne方法来使一个线程阻塞并等待操作完成再调用EndXxx方法来获得操作的结果。
- 循环查询IAsyncResult的IsComplete属性,操作完成后再调用EndXxx方法来获得操作返回的结果。
- 使用 AsyncCallback委托来指定操作完成时要调用的方法,在操作完成后调用的方法中调用EndXxx操作来获得异步操作的结果。
  在上面的4种方式中,第4种方式是APM的首选方式,因为此时不会阻塞执行BeginXxx方法的线程,然而其他三种都会阻塞调用线程,相当于效果和使用同步方法是一样,在实际异步编程中都是使用委托的方式。

看一个简答的例子:

复制代码

using System;
using System.Net;
using System.Threading;class Program
{static DateTime start;static void Main(string[] args){// 用百度分别检索0,1,2,3,4,共检索5次start = DateTime.Now;string strReq = "http://www.baidu.com/s?wd={0}";for (int i = 0; i < 5; i++){var req = WebRequest.Create(string.Format(strReq, i));// 注意这里的BeginGetResponse就是异步方法var res = req.BeginGetResponse(ProcessWebResponse, req);}Thread.Sleep(1000000);}private static void ProcessWebResponse(IAsyncResult result){var req = (WebRequest)result.AsyncState;string strReq = req.RequestUri.AbsoluteUri;using (var res = req.EndGetResponse(result)){Console.Write("检索 {0} 的结果已经返回!\t", strReq.Substring(strReq.Length - 1));Console.WriteLine("耗用时间:{0}毫秒", TimeSpan.FromTicks(DateTime.Now.Ticks - start.Ticks).TotalMilliseconds);}}
}

复制代码

结构相当简单,使用了回调函数获取结果,就不多说了。

 

2. 基于事件的异步模式 (EAP,Event based Asynchronous programming Model)

  在该模式中异步操作由名为“XXXAsync”和“XXXCompleted”的方法/事件表示,例如WebClient.DownloadStringAsync 和 WebClient.DownloadStringCompleted,还有像常用的BackgroundWorker.RunWorkerAsync和BackgroundWorker.RunWorkerCompleted方法。
  EAP 是在 .NET Framework 2.0 版中引入的。使用陈旧的BeginXXX和EndXXX方法无疑是不够优雅的,并且程序员需要写更多的代码,特别是在UI程序中使用不太方便。UI的各种操作基本都是基于事件的,而且通常来说UI线程和子线程之间还需要互相交流,比如说显示进度,警告,相关的消息等等,直接在子线程中访问UI线程上的空间是需要写一些同步代码的。这些操作使用APM处理起来都比较麻烦,而EAP则很好的解决了这些问题,EAP里面最出色的代表就应该是BackgroundWorker类了。
  看一个网上一位仁兄写的下载的小例子:

复制代码

private void btnDownload_Click(object sender, EventArgs e)
{if (bgWorkerFileDownload.IsBusy != true){// 开始异步执行DoWork中指定的任务 bgWorkerFileDownload.RunWorkerAsync();// 创建RequestState对象requestState = new RequestState(downloadPath);requestState.filestream.Seek(DownloadSize, SeekOrigin.Begin);this.btnDownload.Enabled = false;this.btnPause.Enabled = true;}else{MessageBox.Show("正在执行操作,请稍后");}
}private void btnPause_Click(object sender, EventArgs e)
{// 暂停的标准处理方式:先判断标识,然后异步申请暂停if (bgWorkerFileDownload.IsBusy && bgWorkerFileDownload.WorkerSupportsCancellation == true){bgWorkerFileDownload.CancelAsync();}
}// 指定Worker的工作任务,当RunWorkerAsync方法被调用时开始工作
// 这是在子线程中执行的,不允许访问UI上的元素
private void bgWorkerFileDownload_DoWork(object sender, DoWorkEventArgs e)
{// 获取事件源BackgroundWorker bgworker = sender as BackgroundWorker;// 开始下载HttpWebRequest myHttpWebRequest = (HttpWebRequest)WebRequest.Create(txbUrl.Text.Trim());// 断点续传的功能if (DownloadSize != 0){myHttpWebRequest.AddRange(DownloadSize);}requestState.request = myHttpWebRequest;requestState.response = (HttpWebResponse)myHttpWebRequest.GetResponse();requestState.streamResponse = requestState.response.GetResponseStream();int readSize = 0;// 前面讲过的异步取消中子线程的工作:循环并判断标识while (true){if (bgworker.CancellationPending == true){e.Cancel = true;break;}readSize = requestState.streamResponse.Read(requestState.BufferRead, 0, requestState.BufferRead.Length);if (readSize > 0){DownloadSize += readSize;int percentComplete = (int)((float)DownloadSize / (float)totalSize * 100);requestState.filestream.Write(requestState.BufferRead, 0, readSize);// 报告进度,引发ProgressChanged事件的发生bgworker.ReportProgress(percentComplete);}else{break;}}
}// 当Worker执行ReportProgress时回调此函数。此函数在UI线程中执行更新操作进度的任务
// 因为是在在主线程中工作的,可以与UI上的元素交互
private void bgWorkerFileDownload_ProgressChanged(object sender, ProgressChangedEventArgs e)
{this.progressBar1.Value = e.ProgressPercentage;
}// 当Worker结束时触发的回调函数:也许是成功完成的,或是取消了,或者是抛异常了。
// 这个方法是在UI线程中执行,所以可以与UI上的元素交互
private void bgWorkerFileDownload_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{if (e.Error != null){MessageBox.Show(e.Error.Message);requestState.response.Close();}else if (e.Cancelled){MessageBox.Show(String.Format("下载暂停,下载的文件地址为:{0}\n 已经下载的字节数为: {1}字节", downloadPath, DownloadSize));requestState.response.Close();requestState.filestream.Close();this.btnDownload.Enabled = true;this.btnPause.Enabled = false;}else{MessageBox.Show(String.Format("下载已完成,下载的文件地址为:{0},文件的总字节数为: {1}字节", downloadPath, totalSize));this.btnDownload.Enabled = false;this.btnPause.Enabled = false;requestState.response.Close();requestState.filestream.Close();}
}private void GetTotalSize()
{HttpWebRequest myHttpWebRequest = (HttpWebRequest)WebRequest.Create(txbUrl.Text.Trim());HttpWebResponse response = (HttpWebResponse)myHttpWebRequest.GetResponse();totalSize = response.ContentLength;response.Close();
}// 存储申请的状态
public class RequestState
{public int BufferSize = 2048;public byte[] BufferRead;public HttpWebRequest request;public HttpWebResponse response;public Stream streamResponse;public FileStream filestream;public RequestState(string downloadPath){BufferRead = new byte[BufferSize];request = null;streamResponse = null;filestream = new FileStream(downloadPath, FileMode.OpenOrCreate);}
}

复制代码

  上面的例子就是实现了一个可以取消的带断点续传功能的下载器,这是个Winform程序,控件也很简单:一个Label,一个Textbox,两个Button,一个ProgressBar;把这些控件和上面的事件对应绑定即可。  

 

  在.NET 4.0 (C# 4.0)中,并行库(TPL)的加入使得异步编程更加方便快捷,在.NET 4.5 (C# 5.0)中,异步编程将更加方便。

  这里我们先回顾一下C# 4.0中的TPL的用法,看一个简单的小例子:这个例子中只有一个Button和一个Label,点击Button会调用一个函数计算一个结果,这个结果最后会显示到Label上,很简单,我们只看核心的代码:

复制代码

private void button1_Click(object sender, EventArgs e)
{this.button1.Enabled = false;var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext(); //get UI thread context var someTask = Task<int>.Factory.StartNew(() => slowFunc(1, 2)); //create and start the Task someTask.ContinueWith(x =>{this.label1.Text = "Result: " + someTask.Result.ToString();this.button1.Enabled = true;}, uiScheduler);
}private int slowFunc(int a, int b)
{System.Threading.Thread.Sleep(3000);return a + b;
}

复制代码

  上面的slowFunc就是模拟了一个需要大量时间去运行的任务,为了不阻塞UI线程,只能使用Task去异步运行,为了在把结果显示到Label上,代码中我们使用了TaskScheduler.FromCurrentSynchronizationContext()方法同步线程上下文,使得在ContinueWith方法中可以使用UI线程上的控件,这是TPL编程中的一个常用技巧。
  说不上太麻烦,但是感觉上总之不舒服,完全没有同步代码写起来那么自然,简单。从我个人的理解来说,C# 5.0中的async和await正是提高了这方面的用户体验。
  C# 5.0中的async和await特性并没有在IL层面增加了新的成员,所以也可以说是一种语法糖。下面先看看再C# 5.0中如何解决这个问题: 

复制代码

private async void button1_Click(object sender, EventArgs e)
{this.button1.Enabled = false;var someTask = Task<int>.Factory.StartNew(() => slowFunc(1, 2));await someTask;this.label1.Text = "Result: " + someTask.Result.ToString();this.button1.Enabled = true;
}

复制代码

  注意这段代码中的async和await的用法。除了这个事件处理函数,其他的都没有变化。是不是很神奇,完全和同步代码没什么太大的区别,很是简单优雅,完全是同步方式的异步编程
  下面我们就详细的讨论一下async和await这两个关键字。

async和await
  通过使用async修饰符,可将方法、lambda表达式或匿名方法指定为异步。 使用了这个修饰符的方法或表达式,则其称为异步方法,如上面的button1_Click方法就是一个异步方法。
  异步方法提供了一种简便方式来完成可能需要长时间运行的工作,而不必阻塞调用方的线程。 异步方法的调用方(这里就是button1_Click的调用者)可以继续工作,而不必等待异步方法button1_Click完成。 完成这个特性需要使用 await 关键字,以便立即返回,从而允许button1_Click的调用方继续工作或返回到线程的同步上下文(或消息泵)。
  从上面的描述中得到,异步方法更准确的定义应该是:使用async修饰符定义的,且通常包含一个或多个await表达式的方法称为异步方法
  如果async关键字修饰的方法不包含await表达式或语句,则该方法仍将同步执行。 对于这种情况,编译器将会给出警告,因为该情况通常表示程序可能存在错误。 也就是说,单单使用async修饰符的方法还是在同步执行的,只有配合await关键字后方法的部分才开始异步执行。

  await表达式不阻塞主线程。 相反,它告诉编译器去重写异步方法来完成下面几件事:
1. 启动子线程(通常是线程池中的线程)完成await表达式中指定的任务,这是异步执行的真正含义。
2. 将await表达式后面未执行的语句注册为await表达式中执行的任务的后续任务,然后挂起这个异步方法,直接返回到异步方法的调用方。 
3. 当await表达式中执行的任务完成后,子线程结束。
4. 任务寻找到注册的后续任务,恢复异步方法的执行环境,继续执行后续任务,因为已经恢复到异步方法的执行上下文中,所以不存在跨线程的问题。
  看了这个过程,其实与我们使用ContinueWith的那种方式没什么太大的不同。回到上面的button1_Click方法,这下就好理解了,该方法从开始时同步运行,直至到达其第一个await表达式,此时异步的执行Task中指定的方法,然后将button1_Click方法挂起,回到button1_Click的调用者执行其他的代码;直到等待的任务完成后,回到button1_Click中继续执行后续的代码,也就是更新Label的内容。

  这里需要注意几点:
1. async和await只是上下文关键字。 当它们不修饰方法、lambda 表达式或匿名方法时,就不是关键字了,只作为普通的标识符。 
2. 使用async修饰的异步方法的返回类型可以为 Task、Task<TResult> 或 void。 方法不能声明任何 ref 或 out 参数,但是可以调用具有这类参数的方法。
  如果异步方法需要一个 TResult 类型的返回值,则需要应指定 Task<TResult> 作为方法的返回类型。 
  如果当方法完成时未返回有意义的值,则应使用 Task。 对于返回Task的异步方法,当 Task 完成时,任何等待 Task 的所有 await 表达式的计算结果都为 void。
  而使用void作为返回类型的方式主要是来定义事件处理程序,这些处理程序需要此返回类型。 使用void 作为异步方法的返回值时,该异步方法的调用方不能等待,并且无法捕获该方法引发的异常。
3. await表达式的返回值
  如果 await 应用于返回Task<TResult>的方法调用的结果,那么 await 表达式的类型是 TResult。 如果将 await 应用于返回Task的方法调用结果,则 await 表达式的类型无效。看下面的例子中的使用方式:

// 返回Task<TResult>的方法.
TResult result = await AsyncMethodThatReturnsTaskTResult();// 返回一个Task的方法.
await AsyncMethodThatReturnsTask();

4.异常问题
  大多数异步方法返回 Task 或 Task<TResult>。 返回任务的属性承载有关其状态和历史记录的信息,例如任务是否已完成,异步方法是否引发异常或已取消,以及最终结果如何。 await 运算符会访问那些属性。
  如果任务返回异常,await 运算符会再次引发异常。
  如果任务被取消后返回,await 运算符也会再次引发 OperationCanceledException。
  总之,在await外围使用try/catch可以捕获任务中的异常。看一个例子:

复制代码

public class AsyncTest
{static void Main(string[] args){AsyncTest c = new AsyncTest();c.RunAsync();// 模拟其他的工作Thread.Sleep(1000000);}public void RunAsync(){DisplayValue(); //这里不会阻塞Console.WriteLine("RunAsync() End.");}public Task<double> GetValueAsync(double num1, double num2){return Task.Run(() =>{for (int i = 0; i < 1000000; i++){num1 = num1 / num2;if (i == 999999){throw new Exception("Crash");}}return num1;});}public async void DisplayValue(){double result = 0;//此处会开新线程处理GetValueAsync任务,然后方法马上返回try{result = await GetValueAsync(1234.5, 1.0);}catch (Exception){//throw;}//这之后的所有代码都会被封装成委托,在GetValueAsync任务完成时调用Console.WriteLine("Value is : " + result);}
}

复制代码

  但是需要注意一点,如果任务抛出了多个异常(例如,该任务可能是启动了更多的子线程)时,await运算符只能抛出异常中的一个,而且不能确定是哪一个。这时就需要把这些子线程包装到一个Task中,这样这些异常就都会被包装到AggregateException中,看下面例子的做法:

复制代码

public class AsyncTest
{static void Main(string[] args){AsyncTest c = new AsyncTest();c.RunAsync();// 模拟其他的工作Thread.Sleep(1000000);}public void RunAsync(){DisplayValue(); //这里不会阻塞Console.WriteLine("RunAsync() End.");}public async void DisplayValue(){Task all = null;try{await (all = Task.WhenAll(Task.Run(() => { throw new Exception("Ex1"); }), Task.Run(() => { throw new Exception("Ex2"); })));}catch{foreach (var ex in all.Exception.InnerExceptions){Console.WriteLine(ex.Message);}}}
}

复制代码

  当然了,大家也别忘了最后一招杀手锏:TaskScheduler.UnobservedTaskException,使用这个去捕获一些没有处理的异常。
  到此,异步方法就介绍到这里了。最后附上一位网上兄弟写的异步执行一些耗时操作的辅助类:

复制代码

public static class TaskAsyncHelper
{/// <summary>/// 将一个方法function异步运行,在执行完毕时执行回调callback/// </summary>/// <param name="function">异步方法,该方法没有参数,返回类型必须是void</param>/// <param name="callback">异步方法执行完毕时执行的回调方法,该方法没有参数,返回类型必须是void</param>public static async void RunAsync(Action function, Action callback){Func<System.Threading.Tasks.Task> taskFunc = () =>{return System.Threading.Tasks.Task.Run(() =>{function();});};await taskFunc();if (callback != null)callback();}/// <summary>/// 将一个方法function异步运行,在执行完毕时执行回调callback/// </summary>/// <typeparam name="TResult">异步方法的返回类型</typeparam>/// <param name="function">异步方法,该方法没有参数,返回类型必须是TResult</param>/// <param name="callback">异步方法执行完毕时执行的回调方法,该方法参数为TResult,返回类型必须是void</param>public static async void RunAsync<TResult>(Func<TResult> function, Action<TResult> callback){Func<System.Threading.Tasks.Task<TResult>> taskFunc = () =>{return System.Threading.Tasks.Task.Run(() =>{return function();});};TResult rlt = await taskFunc();if (callback != null)callback(rlt);}
}

复制代码

简单实用!

 

推荐链接:
你必须知道的异步编程:http://www.cnblogs.com/zhili/category/475336.html
传统异步编程指导:http://msdn.microsoft.com/zh-cn/library/vstudio/dd997423.aspx
使用async异步编程指导:http://msdn.microsoft.com/zh-cn/library/vstudio/hh191443.aspx

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

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

相关文章

REVERSE-PRACTICE-CTFSHOW-1

REVERSE-PRACTICE-CTFSHOW-1逆向签到题re2逆向4逆向5逆向签到题 ida打开即可得到明文flag re2 附件是一个加密过的flag文本和勒索病毒exe 运行程序&#xff0c;输入1&#xff0c;回车&#xff0c;直接退出&#xff0c;ida分析 选项1的逻辑为&#xff0c;打开flag.txt和enfl…

C#的变迁史09 - C# 5.0 之调用信息增强篇

Caller Information CallerInformation是一个简单的新特性&#xff0c;包括三个新引入的Attribute&#xff0c;使用它们可以用来获取方法调用者的信息&#xff0c; 这三个Attribute在System.Runtime.CompilerServices命名空间下&#xff0c;分别叫做CallerMemberNameAttribute&…

REVERSE-PRACTICE-CTFSHOW-2

REVERSE-PRACTICE-CTFSHOW-2re3红包题 武穆遗书数学不及格flag白给re3 main函数&#xff0c;分析可知&#xff0c;将输入的字符串按十六进制转成数字&#xff0c;写到v5&#xff0c;赋给v17[6] 当i等于6时&#xff0c;v16会加上输入的值&#xff0c;然后进入循环&#xff0c;最…

C#的变迁史10 - C# 5.0 之其他增强篇

1. 内置zip压缩与解压   Zip是最为常用的文件压缩格式之一&#xff0c;也被几乎所有操作系统支持。在之前&#xff0c;使用程序去进行zip压缩和解压要靠第三方组件去支持&#xff0c;这一点在.NET4.5中已有所改观&#xff0c;Zip压缩和解压功能已经内置于框架本身。这个功能使…

REVERSE-PRACTICE-CTFSHOW-3

REVERSE-PRACTICE-CTFSHOW-3签退神光签到baby_gay签退 .pyc文件&#xff0c;uncompyle6反编译&#xff0c;得到python源码&#xff0c;分析写在源码注释中 先变表base64&#xff0c;再凯撒加密&#xff0c;向后移动2位 import string c_charset string.ascii_uppercase str…

REVERSE-PRACTICE-CTFSHOW-4

REVERSE-PRACTICE-CTFSHOW-4encodeEasyBJD hamburger competitionJustREencode elf文件&#xff0c;upx脱壳&#xff0c;ida分析 交叉引用字符串"Please input your flag:"&#xff0c;来到sub_804887C函数 输入经过三次变换&#xff0c;先是变表base64&#xff0c;…

CSS 基础框盒模型介绍

当对一个文档进行布局&#xff08;lay out&#xff09;的时候&#xff0c;浏览器的渲染引擎会根据标准之一的 CSS 基础框盒模型&#xff08;CSS basic box model&#xff09;&#xff0c;将所有元素表示为一个个矩形的盒子&#xff08;box&#xff09;。CSS 决定这些盒子的大小…

REVERSE-PRACTICE-CTFSHOW-5

REVERSE-PRACTICE-CTFSHOW-5re2_归心Mud[吃鸡杯]ezmore[吃鸡杯]有手就行re2_归心 exe程序&#xff0c;运行后要求输入flag&#xff0c;ida分析 函数窗没找到主逻辑函数&#xff0c;shiftF12看字符串窗口 发现有java/lang/String&#xff0c;com/exe4j/runtime/WinLauncher等字…

PWN-PRACTICE-BUUCTF-2

PWN-PRACTICE-BUUCTF-2pwn1_sctf_2016jarvisoj_level0ciscn_2019_c_1[第五空间2019 决赛]PWN5pwn1_sctf_2016 main函数中执行vuln函数 fgets限制了输入的长度&#xff0c;不足以构成栈溢出 通过将输入中的字符"I"替换成"you"&#xff0c;增加长度&#xf…

PWN-PRACTICE-BUUCTF-3

PWN-PRACTICE-BUUCTF-3[OGeek2019]babyropciscn_2019_n_8get_started_3dsctf_2016jarvisoj_level2[OGeek2019]babyrop 简单的ret2libc&#xff0c;构造rop main函数中读取一个随机数到buf中&#xff0c;传入sub_804871F 用"\x00"来绕过strlen和strncmp&#xff0c;b…

c#中常用集合类和集合接口之接口系列【转】

常用集合接口系列&#xff1a;http://www.cnblogs.com/fengxiaojiu/p/7997704.html 常用集合类系列&#xff1a;http://www.cnblogs.com/fengxiaojiu/p/7997541.html 大多数集合都在System.Collections&#xff0c;System.Collections.Generic两个命名空间。其中System.Colle…

PWN-PRACTICE-BUUCTF-4

PWN-PRACTICE-BUUCTF-4ciscn_2019_en_2bjdctf_2020_babystacknot_the_same_3dsctf_2016[HarekazeCTF2019]baby_ropciscn_2019_en_2 这题和ciscn_2019_c_1一模一样 栈溢出ret2libc&#xff0c;encrypt函数里的异或运算不用管 from pwn import * context.log_level"debug&…

PWN-PRACTICE-BUUCTF-5

PWN-PRACTICE-BUUCTF-5jarvisoj_level2_x64ciscn_2019_n_5others_shellcodeciscn_2019_ne_5jarvisoj_level2_x64 这题和[HarekazeCTF2019]baby_rop几乎一模一样 from pwn import * #context.log_level"debug" ioremote(node4.buuoj.cn,27023) elfELF(./level2_x64)…

Scrum敏捷开发沉思录

计算机科学的诞生&#xff0c;是世人为了用数字手段解决实际生活中的问题。随着时代的发展&#xff0c;技术的进步&#xff0c;人们对于现实世界中的问题理解越来越深刻&#xff0c;描述也越来越抽象&#xff0c;于是对计算机软件的需求也越来越高&#xff0c;越来越复杂&#…

PWN-PRACTICE-BUUCTF-6

PWN-PRACTICE-BUUCTF-6铁人三项(第五赛区)_2018_ropbjdctf_2020_babyropbabyheap_0ctf_2017pwn2_sctf_2016铁人三项(第五赛区)_2018_rop vulnerable_function函数中read构成栈溢出&#xff0c;ret2libc from pwn import * context.log_level"debug" ioremote(node4…

PWN-PRACTICE-BUUCTF-7

PWN-PRACTICE-BUUCTF-7jarvisoj_fmciscn_2019_s_3SROP解法ret2csu解法bjdctf_2020_babystack2[HarekazeCTF2019]baby_rop2jarvisoj_fm 格式化字符串漏洞&#xff0c;可以测出我们的输入在栈上的偏移为11 自己构造或者使用fmtstr_payload构造payload均可&#xff0c;目标是让x4…

Axure教程 axure新手入门基础(3) 简单易上手

(三)Axure rp元件的触发事件 l OnClick(点击时): 鼠标点击事件&#xff0c;除了动态面板的所有的其他元件的点击时触发。比如点击按钮。 l OnMouseEnter(鼠标移入时): 鼠标进入到某个元件范围时触发&#xff0c;比如当鼠标移到某张图片时显示该图片的介绍。 l OnMouseOut(鼠标移…

使用Nuget 安装指定版本package或者更新package到指定版本

最近在琢磨MVC和EntityFramework,使用的VS是2013版的&#xff0c;在Nuget的GUI界面下安装了EntityFramework(默认安装最新版的&#xff0c;怎么安装指定版本还没找到),按照MVC的示例项目MusicStore逐步做的过程中发现MVC4不支持EntityFramwok 6。 尝试去更新MVC版本各种失败&a…

CSS一个元素同时使用多个类选择器(class selector)

CSS类选择器参考手册 一个元素同时使用多个类选择器 CSS中类选择器用点号表示。实际项目中一个div元素为了能被多个样式表匹配到&#xff08;样式复用&#xff09;&#xff0c;通常div的class中由好几段组成&#xff0c;如<div class"user login">能被.user和…

css 块元素、内联元素、内联块元素

元素就是标签&#xff0c;布局中常用的有三种标签&#xff0c;块元素、内联元素、内联块元素&#xff0c;了解这三种元素的特性&#xff0c;才能熟练的进行页面布局。 块元素&#xff1a; 块元素&#xff0c;也可以称为行元素&#xff0c;布局中常用的标签&#xff0c;如&…