日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > C# >内容正文

C#

C#细说多线程(下)

發(fā)布時間:2023/12/10 C# 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C#细说多线程(下) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

本文主要從線程的基礎(chǔ)用法,CLR線程池當(dāng)中工作者線程與I/O線程的開發(fā),并行操作PLINQ等多個方面介紹多線程的開發(fā)。

其中委托的BeginInvoke方法以及回調(diào)函數(shù)最為常用。
而 I/O線程可能容易遭到大家的忽略,其實在開發(fā)多線程系統(tǒng),更應(yīng)該多留意I/O線程的操作。特別是在ASP.NET開發(fā)當(dāng)中,可能更多人只會留意在客戶端使用Ajax或者在服務(wù)器端使用UpdatePanel。其實合理使用I/O線程在通訊項目或文件下載時,能盡量降低IIS的壓力。
并行編程是Framework4.0中極力推廣的異步操作方式,更值得更深入地學(xué)習(xí)。
希望本篇文章能對各位的學(xué)習(xí)研究有所幫助,當(dāng)中有所錯漏的地方敬請點評。

?

?

目錄

一、線程的定義

二、線程的基礎(chǔ)知識

三、以ThreadStart方式實現(xiàn)多線程

四、CLR線程池的工作者線程

五、CLR線程池的I/O線程

六、異步 SqlCommand

七、并行編程與PLINQ

八、計時器與鎖

?

?

五、CLR線程池的I/O線程

在前一節(jié)所介紹的線程都屬于CLR線程池的工作者線程,這一節(jié)開始為大家介紹一下CLR線程池的I/O線程

I/O 線程是.NET專為訪問外部資源所設(shè)置的一種線程,因為訪問外部資源常常要受到外界因素的影響,為了防止讓主線程受影響而長期處于阻塞狀態(tài),.NET為多個I/O操作都建立起了異步方法,例如:FileStream、TCP/IP、WebRequest、WebService等等,而且每個異步方法的使用方式都非常類似,都是以BeginXXX為開始,以EndXXX結(jié)束,下面為大家一一解說。

?

5.1? 異步讀寫 FileStream

需要在 FileStream 異步調(diào)用 I/O線程,必須使用以下構(gòu)造函數(shù)建立 FileStream 對象,并把useAsync設(shè)置為 true。

FileStream stream = new FileStream ( string path, FileMode mode, FileAccess access, FileShare share, int bufferSize,bool useAsync ) ;

其中?path?是文件的相對路徑或絕對路徑;?mode?確定如何打開或創(chuàng)建文件;?access?確定訪問文件的方式; share 確定文件如何進程共享; bufferSize 是代表緩沖區(qū)大小,一般默認最小值為8,在啟動異步讀取或?qū)懭霑r,文件大小一般大于緩沖大小; userAsync代表是否啟動異步I/O線程。

注意:當(dāng)使用 BeginRead 和 BeginWrite 方法在執(zhí)行大量讀或?qū)憰r效果更好,但對于少量的讀/寫,這些方法速度可能比同步讀取還要慢,因為進行線程間的切換需要大量時間。

?

5.1.1 異步寫入

FileStream中包含BeginWrite、EndWrite 方法可以啟動I/O線程進行異步寫入。

public override IAsyncResult BeginWrite ( byte[] array, int offset, int numBytes, AsyncCallback userCallback, Object stateObject )
public override void EndWrite (IAsyncResult asyncResult )

?

BeginWrite 返回值為IAsyncResult, 使用方式與委托的BeginInvoke方法相似,最好就是使用回調(diào)函數(shù),避免線程阻塞。在最后兩個參數(shù)中,參數(shù)AsyncCallback用于綁定回調(diào)函數(shù); 參數(shù)Object用于傳遞外部數(shù)據(jù)。要注意一點:AsyncCallback所綁定的回調(diào)函數(shù)必須是帶單個 IAsyncResult 參數(shù)的無返回值方法。
在例子中,把FileStream作為外部數(shù)據(jù)傳遞到回調(diào)函數(shù)當(dāng)中,然后在回調(diào)函數(shù)中利用IAsyncResult.AsyncState獲取FileStream對象,最后通過FileStream.EndWrite(IAsyncResult)結(jié)束寫入。

1 class Program
2 {
3 static void Main(string[] args)
4 {
5 //把線程池的最大值設(shè)置為1000
6 ThreadPool.SetMaxThreads(1000, 1000);
7 ThreadPoolMessage("Start");
8
9 //新立文件File.sour
10 FileStream stream = new FileStream("File.sour", FileMode.OpenOrCreate,
11 FileAccess.ReadWrite,FileShare.ReadWrite,1024,true);
12 byte[] bytes = new byte[16384];
13 string message = "An operating-system ThreadId has no fixed relationship........";
14 bytes = Encoding.Unicode.GetBytes(message);
15
16 //啟動異步寫入
17 stream.BeginWrite(bytes, 0, (int)bytes.Length,new AsyncCallback(Callback),stream);
18 stream.Flush();
19
20 Console.ReadKey();
21 }
22
23 static void Callback(IAsyncResult result)
24 {
25 //顯示線程池現(xiàn)狀
26 Thread.Sleep(200);
27 ThreadPoolMessage("AsyncCallback");
28 //結(jié)束異步寫入
29 FileStream stream = (FileStream)result.AsyncState;
30 stream.EndWrite(result);
31 stream.Close();
32 }
33
34 //顯示線程池現(xiàn)狀
35 static void ThreadPoolMessage(string data)
36 {
37 int a, b;
38 ThreadPool.GetAvailableThreads(out a, out b);
39 string message = string.Format("{0}\n CurrentThreadId is {1}\n "+
40 "WorkerThreads is:{2} CompletionPortThreads is :{3}",
41 data, Thread.CurrentThread.ManagedThreadId, a.ToString(), b.ToString());
42 Console.WriteLine(message);
43 }
44 }

由輸出結(jié)果可以看到,在使用FileStream.BeginWrite方法后,系統(tǒng)將自動啟動CLR線程池中I/O線程。



5.1.2 異步讀取

FileStream 中包含 BeginRead 與 EndRead 可以異步調(diào)用I/O線程進行讀取。

public override IAsyncResult BeginRead ( byte[] array,int offset,int numBytes, AsyncCallback userCallback,Object stateObject)
public override int EndRead(IAsyncResult asyncResult)

?

其使用方式與BeginWrite和EndWrite相似,AsyncCallback用于綁定回調(diào)函數(shù); Object用于傳遞外部數(shù)據(jù)。在回調(diào)函數(shù)只需要使用IAsyncResut.AsyncState就可獲取外部數(shù)據(jù)。EndWrite 方法會返回從流讀取到的字節(jié)數(shù)量。

首先定義 FileData 類,里面包含F(xiàn)ileStream對象,byte[] 數(shù)組和長度。然后把FileData對象作為外部數(shù)據(jù)傳到回調(diào)函數(shù),在回調(diào)函數(shù)中,把IAsyncResult.AsyncState強制轉(zhuǎn)換為FileData,然后通過FileStream.EndRead(IAsyncResult)結(jié)束讀取。最后比較一下長度,若讀取到的長度與輸入的數(shù)據(jù)長度不一至,則拋出異常。

1 class Program
2 {
3 public class FileData
4 {
5 public FileStream Stream;
6 public int Length;
7 public byte[] ByteData;
8 }
9
10 static void Main(string[] args)
11 {
12 //把線程池的最大值設(shè)置為1000
13 ThreadPool.SetMaxThreads(1000, 1000);
14 ThreadPoolMessage("Start");
15 ReadFile();
16
17 Console.ReadKey();
18 }
19
20 static void ReadFile()
21 {
22 byte[] byteData=new byte[80961024];
23 FileStream stream = new FileStream("File1.sour", FileMode.OpenOrCreate,
24 FileAccess.ReadWrite, FileShare.ReadWrite, 1024, true);
25
26 //把FileStream對象,byte[]對象,長度等有關(guān)數(shù)據(jù)綁定到FileData對象中,以附帶屬性方式送到回調(diào)函數(shù)
27 FileData fileData = new FileData();
28 fileData.Stream = stream;
29 fileData.Length = (int)stream.Length;
30 fileData.ByteData = byteData;
31
32 //啟動異步讀取
33 stream.BeginRead(byteData, 0, fileData.Length, new AsyncCallback(Completed), fileData);
34 }
35
36 static void Completed(IAsyncResult result)
37 {
38 ThreadPoolMessage("Completed");
39
40 //把AsyncResult.AsyncState轉(zhuǎn)換為FileData對象,以FileStream.EndRead完成異步讀取
41 FileData fileData = (FileData)result.AsyncState;
42 int length=fileData.Stream.EndRead(result);
43 fileData.Stream.Close();
44
45 //如果讀取到的長度與輸入長度不一致,則拋出異常
46 if (length != fileData.Length)
47 throw new Exception("Stream is not complete!");
48
49 string data=Encoding.ASCII.GetString(fileData.ByteData, 0, fileData.Length);
50 Console.WriteLine(data.Substring(2,22));
51 }
52
53 //顯示線程池現(xiàn)狀
54 static void ThreadPoolMessage(string data)
55 {
56 int a, b;
57 ThreadPool.GetAvailableThreads(out a, out b);
58 string message = string.Format("{0}\n CurrentThreadId is {1}\n "+
59 "WorkerThreads is:{2} CompletionPortThreads is :{3}",
60 data, Thread.CurrentThread.ManagedThreadId, a.ToString(), b.ToString());
61 Console.WriteLine(message);
62 }
63
64 }

由輸出結(jié)果可以看到,在使用FileStream.BeginRead方法后,系統(tǒng)將自動啟動CLR線程池中I/O線程。

?

注意:如果你看到的測試結(jié)果正好相反:工作者線程為999,I/O線程為1000,這是因為FileStream的文件容量小于緩沖值1024所致的。此時文件將會一次性讀取或?qū)懭?#xff0c;而系統(tǒng)將啟動工作者線程而非I/O線程來處理回調(diào)函數(shù)。

?

?

5.2 異步操作TCP/IP套接字

在介紹 TCP/IP 套接字前先簡單介紹一下 NetworkStream 類,它是用于網(wǎng)絡(luò)訪問的基礎(chǔ)數(shù)據(jù)流。 NetworkStream 提供了好幾個方法控制套接字數(shù)據(jù)的發(fā)送與接收, 其中BeginRead、EndRead、BeginWrite、EndWrite 能夠?qū)崿F(xiàn)異步操作,而且異步線程是來自于CLR線程池的I/O線程。

public override int ReadByte ()
public override int Read (byte[] buffer,int offset, int size)

public override void WriteByte (byte value)
public override void Write (byte[] buffer,int offset, int size)

public override IAsyncResult BeginRead (byte [] buffer, int offset, int size,? AsyncCallback callback, Object state )
public override int EndRead(IAsyncResult result)

public override IAsyncResult BeginWrite (byte [] buffer, int offset, int size,? AsyncCallback callback, Object state )
public override void EndWrite(IAsyncResult result)

?

若要創(chuàng)建 NetworkStream,必須提供已連接的 Socket。而在.NET中使用TCP/IP套接字不需要直接與Socket打交道,因為.NET把Socket的大部分操作都放在System.Net.TcpListener和System.Net.Sockets.TcpClient里面,這兩個類大大地簡化了Socket的操作。一般套接字對象Socket包含一個Accept()方法,此方法能產(chǎn)生阻塞來等待客戶端的請求,而在TcpListener類里也包含了一個相似的方法 public TcpClient AcceptTcpClient()用于等待客戶端的請求。此方法將會返回一個TcpClient 對象,通過 TcpClient 的 public NetworkStream GetStream()方法就能獲取NetworkStream對象,控制套接字數(shù)據(jù)的發(fā)送與接收。

?

下面以一個例子說明異步調(diào)用TCP/IP套接字收發(fā)數(shù)據(jù)的過程。

首先在服務(wù)器端建立默認地址127.0.0.1用于收發(fā)信息,使用此地址與端口500新建TcpListener對象,調(diào)用TcpListener.Start 偵聽傳入的連接請求,再使用一個死循環(huán)來監(jiān)聽信息。

在ChatClient類包括有接收信息與發(fā)送信息兩個功能:當(dāng)接收到客戶端請求時,它會利用 NetworkStream.BeginRead 讀取客戶端信息,并在回調(diào)函數(shù)ReceiveAsyncCallback中輸出信息內(nèi)容,若接收到的信息的大小小于1時,它將會拋出一個異常。當(dāng)信息成功接收后,再使用 NetworkStream.BeginWrite 方法回饋信息到客戶端

1 class Program
2 {
3 static void Main(string[] args)
4 {
5 //設(shè)置CLR線程池最大線程數(shù)
6 ThreadPool.SetMaxThreads(1000, 1000);
7
8 //默認地址為127.0.0.1
9 IPAddress ipAddress = IPAddress.Parse("127.0.0.1");
10 TcpListener tcpListener = new TcpListener(ipAddress, 500);
11 tcpListener.Start();
12
13 //以一個死循環(huán)來實現(xiàn)監(jiān)聽
14 while (true)
15 { //調(diào)用一個ChatClient對象來實現(xiàn)監(jiān)聽
16 ChatClient chatClient = new ChatClient(tcpListener.AcceptTcpClient());
17 }
18 }
19 }
20
21 public class ChatClient
22 {
23 static TcpClient tcpClient;
24 static byte[] byteMessage;
25 static string clientEndPoint;
26
27 public ChatClient(TcpClient tcpClient1)
28 {
29 tcpClient = tcpClient1;
30 byteMessage = new byte[tcpClient.ReceiveBufferSize];
31
32 //顯示客戶端信息
33 clientEndPoint = tcpClient.Client.RemoteEndPoint.ToString();
34 Console.WriteLine("Client's endpoint is " + clientEndPoint);
35
36 //使用NetworkStream.BeginRead異步讀取信息
37 NetworkStream networkStream = tcpClient.GetStream();
38 networkStream.BeginRead(byteMessage, 0, tcpClient.ReceiveBufferSize,
39 new AsyncCallback(ReceiveAsyncCallback), null);
40 }
41
42 public void ReceiveAsyncCallback(IAsyncResult iAsyncResult)
43 {
44 //顯示CLR線程池狀態(tài)
45 Thread.Sleep(100);
46 ThreadPoolMessage("\nMessage is receiving");
47
48 //使用NetworkStream.EndRead結(jié)束異步讀取
49 NetworkStream networkStreamRead = tcpClient.GetStream();
50 int length=networkStreamRead.EndRead(iAsyncResult);
51
52 //如果接收到的數(shù)據(jù)長度少于1則拋出異常
53 if (length < 1)
54 {
55 tcpClient.GetStream().Close();
56 throw new Exception("Disconnection!");
57 }
58
59 //顯示接收信息
60 string message = Encoding.UTF8.GetString(byteMessage, 0, length);
61 Console.WriteLine("Message:" + message);
62
63 //使用NetworkStream.BeginWrite異步發(fā)送信息
64 byte[] sendMessage = Encoding.UTF8.GetBytes("Message is received!");
65 NetworkStream networkStreamWrite=tcpClient.GetStream();
66 networkStreamWrite.BeginWrite(sendMessage, 0, sendMessage.Length,
67 new AsyncCallback(SendAsyncCallback), null);
68 }
69
70 //把信息轉(zhuǎn)換成二進制數(shù)據(jù),然后發(fā)送到客戶端
71 public void SendAsyncCallback(IAsyncResult iAsyncResult)
72 {
73 //顯示CLR線程池狀態(tài)
74 Thread.Sleep(100);
75 ThreadPoolMessage("\nMessage is sending");
76
77 //使用NetworkStream.EndWrite結(jié)束異步發(fā)送
78 tcpClient.GetStream().EndWrite(iAsyncResult);
79
80 //重新監(jiān)聽
81 tcpClient.GetStream().BeginRead(byteMessage, 0, tcpClient.ReceiveBufferSize,
82 new AsyncCallback(ReceiveAsyncCallback), null);
83 }
84
85 //顯示線程池現(xiàn)狀
86 static void ThreadPoolMessage(string data)
87 {
88 int a, b;
89 ThreadPool.GetAvailableThreads(out a, out b);
90 string message = string.Format("{0}\n CurrentThreadId is {1}\n " +
91 "WorkerThreads is:{2} CompletionPortThreads is :{3}\n",
92 data, Thread.CurrentThread.ManagedThreadId, a.ToString(), b.ToString());
93
94 Console.WriteLine(message);
95 }
96 }

而在客戶端只是使用簡單的開發(fā)方式,利用TcpClient連接到服務(wù)器端,然后調(diào)用NetworkStream.Write方法發(fā)送信息,最后調(diào)用NetworkStream.Read方法讀取回饋信息

1 static void Main(string[] args)
2 {
3 //連接服務(wù)端
4 TcpClient tcpClient = new TcpClient("127.0.0.1", 500);
5
6 //發(fā)送信息
7 NetworkStream networkStream = tcpClient.GetStream();
8 byte[] sendMessage = Encoding.UTF8.GetBytes("Client request connection!");
9 networkStream.Write(sendMessage, 0, sendMessage.Length);
10 networkStream.Flush();
11
12 //接收信息
13 byte[] receiveMessage=new byte[1024];
14 int count=networkStream.Read(receiveMessage, 0,1024);
15 Console.WriteLine(Encoding.UTF8.GetString(receiveMessage));
16 Console.ReadKey();
17 }

注意觀察運行結(jié)果,服務(wù)器端的異步操作線程都是來自于CLR線程池的I/O線程



5.3 異步WebRequest

System.Net.WebRequest?是 .NET 為實現(xiàn)訪問 Internet?的 “請求/響應(yīng)模型” 而開發(fā)的一個?abstract?基類,?它主要有三個子類:FtpWebRequest、HttpWebRequest、FileWebRequest。當(dāng)使用WebRequest.Create(string uri)創(chuàng)建對象時,應(yīng)用程序就可以根據(jù)請求協(xié)議判斷實現(xiàn)類來進行操作。FileWebRequest、FtpWebRequest、HttpWebRequest 各有其作用:FileWebRequest 使用 “file://路徑” 的URI方式實現(xiàn)對本地資源和內(nèi)部文件的請求/響應(yīng)、FtpWebRequest 使用FTP文件傳輸協(xié)議實現(xiàn)文件請求/響應(yīng)、HttpWebRequest 用于處理HTTP的頁面請求/響應(yīng)。由于使用方法相類似,下面就以常用的HttpWebRequest為例子介紹一下異步WebRequest的使用方法。

在使用ASP.NET開發(fā)網(wǎng)站的時候,往往會忽略了HttpWebRequest的使用,因為開發(fā)都假設(shè)客戶端是使用瀏覽器等工具去閱讀頁面的。但如果你對REST開發(fā)方式有所了解,那對 HttpWebRequest 就應(yīng)該非常熟悉。它可以在路徑參數(shù)、頭文件、頁面主體、Cookie 等多處地方加入請求條件,然后對回復(fù)數(shù)據(jù)進行適當(dāng)處理。HttpWebRequest 包含有以下幾個常用方法用于處理請求/響應(yīng):

public override Stream GetRequestStream ()
public override WebResponse GetResponse ()

public override IAsyncResult BeginGetRequestStream ( AsyncCallback callback, Object state )
public override Stream EndGetRequestStream ( IAsyncResult asyncResult )
public override IAsyncResult BeginGetResponse ( AsyncCallback callback, Object state )
public override WebResponse EndGetResponse ( IAsyncResult asyncResult )

其中BeginGetRequestStream、EndGetRequestStream 用于異步向HttpWebRequest對象寫入請求信息;? BeginGetResponse、EndGetResponse 用于異步發(fā)送頁面請求并獲取返回信息。使用異步方式操作Internet的“請求/響應(yīng)”,避免主線程長期處于等待狀態(tài),而操作期間異步線程是來自CLR線程池的I/O線程。

注意:請求與響應(yīng)不能使用同步與異步混合開發(fā)模式,即當(dāng)請求寫入使用GetRequestStream同步模式,即使響應(yīng)使用BeginGetResponse異步方法,操作也與GetRequestStream方法在于同一線程內(nèi)。

下面以簡單的例子介紹一下異步請求的用法。

首先為Person類加上可序列化特性,在服務(wù)器端建立Hanlder.ashx,通過Request.InputStream 獲取到請求數(shù)據(jù)并把數(shù)據(jù)轉(zhuǎn)化為String對象,此實例中數(shù)據(jù)是以 “Id:1” 的形式實現(xiàn)傳送的。然后根據(jù)Id查找對應(yīng)的Person對象,并把Person對象寫入Response.OutStream 中返還到客戶端。

在客戶端先把 HttpWebRequird.Method 設(shè)置為 "post",使用異步方式通過BeginGetRequireStream獲取請求數(shù)據(jù)流,然后寫入請求數(shù)據(jù) “Id:1”。再使用異步方法BeginGetResponse 獲取回復(fù)數(shù)據(jù),最后把數(shù)據(jù)反序列化為Person對象顯示出來。

注意:HttpWebRequire.Method默認為get,在寫入請求前必須把HttpWebRequire.Method設(shè)置為post,否則在使用BeginGetRequireStream 獲取請求數(shù)據(jù)流的時候,系統(tǒng)就會發(fā)出 “無法發(fā)送具有此謂詞類型的內(nèi)容正文" 的異常。

Model

1 namespace Model
2 {
3 [Serializable]
4 public class Person
5 {
6 public int ID
7 {
8 get;
9 set;
10 }
11 public string Name
12 {
13 get;
14 set;
15 }
16 public int Age
17 {
18 get;
19 set;
20 }
21 }
22 }

?

服務(wù)器端

1 public class Handler : IHttpHandler {
2
3 public void ProcessRequest(HttpContext context)
4 {
5 //把信息轉(zhuǎn)換為String,找出輸入條件Id
6 byte[] bytes=new byte[1024];
7 int length=context.Request.InputStream.Read(bytes,0,1024);
8 string condition = Encoding.Default.GetString(bytes);
9 int id = int.Parse(condition.Split(new string[] { ":" },
10 StringSplitOptions.RemoveEmptyEntries)[1]);
11
12 //根據(jù)Id查找對應(yīng)Person對象
13 var person = GetPersonList().Where(x => x.ID == id).First();
14
15 //所Person格式化為二進制數(shù)據(jù)寫入OutputStream
16 BinaryFormatter formatter = new BinaryFormatter();
17 formatter.Serialize(context.Response.OutputStream, person);
18 }
19
20 //模擬源數(shù)據(jù)
21 private IList<Person> GetPersonList()
22 {
23 var personList = new List<Person>();
24
25 var person1 = new Person();
26 person1.ID = 1;
27 person1.Name = "Leslie";
28 person1.Age = 30;
29 personList.Add(person1);
30 ...........
31 return personList;
32 }
33
34 public bool IsReusable
35 {
36 get { return true;}
37 }
38 }

客戶端

1 class Program
2 {
3 static void Main(string[] args)
4 {
5 ThreadPool.SetMaxThreads(1000, 1000);
6 Request();
7 Console.ReadKey();
8 }
9
10 static void Request()
11 {
12 ThreadPoolMessage("Start");
13 //使用WebRequest.Create方法建立HttpWebRequest對象
14 HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(
15 "http://localhost:5700/Handler.ashx");
16 webRequest.Method = "post";
17
18 //對寫入數(shù)據(jù)的RequestStream對象進行異步請求
19 IAsyncResult result=webRequest.BeginGetRequestStream(
20 new AsyncCallback(EndGetRequestStream),webRequest);
21 }
22
23 static void EndGetRequestStream(IAsyncResult result)
24 {
25 ThreadPoolMessage("RequestStream Complete");
26 //獲取RequestStream
27 HttpWebRequest webRequest = (HttpWebRequest)result.AsyncState;
28 Stream stream=webRequest.EndGetRequestStream(result);
29
30 //寫入請求條件
31 byte[] condition = Encoding.Default.GetBytes("Id:1");
32 stream.Write(condition, 0, condition.Length);
33
34 //異步接收回傳信息
35 IAsyncResult responseResult = webRequest.BeginGetResponse(
36 new AsyncCallback(EndGetResponse), webRequest);
37 }
38
39 static void EndGetResponse(IAsyncResult result)
40 {
41 //顯出線程池現(xiàn)狀
42 ThreadPoolMessage("GetResponse Complete");
43
44 //結(jié)束異步請求,獲取結(jié)果
45 HttpWebRequest webRequest = (HttpWebRequest)result.AsyncState;
46 WebResponse webResponse = webRequest.EndGetResponse(result);
47
48 //把輸出結(jié)果轉(zhuǎn)化為Person對象
49 Stream stream = webResponse.GetResponseStream();
50 BinaryFormatter formatter = new BinaryFormatter();
51 var person=(Person)formatter.Deserialize(stream);
52 Console.WriteLine(string.Format("Person Id:{0} Name:{1} Age:{2}",
53 person.ID, person.Name, person.Age));
54 }
55
56 //顯示線程池現(xiàn)狀
57 static void ThreadPoolMessage(string data)
58 {
59 int a, b;
60 ThreadPool.GetAvailableThreads(out a, out b);
61 string message = string.Format("{0}\n CurrentThreadId is {1}\n " +
62 "WorkerThreads is:{2} CompletionPortThreads is :{3}\n",
63 data, Thread.CurrentThread.ManagedThreadId, a.ToString(), b.ToString());
64
65 Console.WriteLine(message);
66 }
67 }

從運行結(jié)果可以看到,BeginGetRequireStream、BeginGetResponse方法是使用CLR線程池的I/O線程。

?

?

5.4 異步調(diào)用WebService

相比TCP/IP套接字,在使用WebService的時候,服務(wù)器端需要更復(fù)雜的操作處理,使用時間往往會更長。為了避免客戶端長期處于等待狀態(tài),在配置服務(wù)引用時選擇 “生成異步操作”,系統(tǒng)可以自動建立異步調(diào)用的方式。

以.NET 2.0以前,系統(tǒng)都是使用ASMX來設(shè)計WebService,而近年來WCF可說是火熱登場,下面就以WCF為例子簡單介紹一下異步調(diào)用WebService的例子。

由于系統(tǒng)可以自動生成異步方法,使用起來非常簡單,首先在服務(wù)器端建立服務(wù)ExampleService,里面包含方法Method。客戶端引用此服務(wù)時,選擇 “生成異步操作”。然后使用 BeginMethod 啟動異步方法, 在回調(diào)函數(shù)中調(diào)用EndMethod結(jié)束異步調(diào)用。

服務(wù)端

1 [ServiceContract]
2 public interface IExampleService
3 {
4 [OperationContract]
5 string Method(string name);
6 }
7
8 public class ExampleService : IExampleService
9 {
10 public string Method(string name)
11 {
12 return "Hello " + name;
13 }
14 }
15
16 class Program
17 {
18 static void Main(string[] args)
19 {
20 ServiceHost host = new ServiceHost(typeof(ExampleService));
21 host.Open();
22 Console.ReadKey();
23 host.Close();
24 }
25 }
26
27 <configuration>
28 <system.serviceModel>
29 <services>
30 <service name="Example.ExampleService">
31 <endpoint address="" binding="wsHttpBinding" contract="Example.IExampleService">
32 <identity>
33 <dns value="localhost" />
34 </identity>
35 </endpoint>
36 <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
37 <host>
38 <baseAddresses>
39 <add baseAddress="http://localhost:7200/Example/ExampleService/" />
40 </baseAddresses>
41 </host>
42 </service>
43 </services>
44 </system.serviceModel>
45 </configuration>

客戶端

1 class Program
2 {
3 static void Main(string[] args)
4 {
5 //設(shè)置最大線程數(shù)
6 ThreadPool.SetMaxThreads(1000, 1000);
7 ThreadPoolMessage("Start");
8
9 //建立服務(wù)對象,異步調(diào)用服務(wù)方法
10 ExampleServiceReference.ExampleServiceClient exampleService = new
11 ExampleServiceReference.ExampleServiceClient();
12 exampleService.BeginMethod("Leslie",new AsyncCallback(AsyncCallbackMethod),
13 exampleService);
14 Console.ReadKey();
15 }
16
17 static void AsyncCallbackMethod(IAsyncResult result)
18 {
19 Thread.Sleep(1000);
20 ThreadPoolMessage("Complete");
21 ExampleServiceReference.ExampleServiceClient example =
22 (ExampleServiceReference.ExampleServiceClient)result.AsyncState;
23 string data=example.EndMethod(result);
24 Console.WriteLine(data);
25 }
26
27 //顯示線程池現(xiàn)狀
28 static void ThreadPoolMessage(string data)
29 {
30 int a, b;
31 ThreadPool.GetAvailableThreads(out a, out b);
32 string message = string.Format("{0}\n CurrentThreadId is {1}\n " +
33 "WorkerThreads is:{2} CompletionPortThreads is :{3}\n",
34 data, Thread.CurrentThread.ManagedThreadId, a.ToString(), b.ToString());
35
36 Console.WriteLine(message);
37 }
38 }
39
40 <configuration>
41 <system.serviceModel>
42 <bindings>
43 <wsHttpBinding>
44 <binding name="WSHttpBinding_IExampleService" closeTimeout="00:01:00"
45 openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
46 bypassProxyOnLocal="false" transactionFlow="false"
47 hostNameComparisonMode="StrongWildcard" maxBufferPoolSize="524288"
48 maxReceivedMessageSize="65536" messageEncoding="Text" textEncoding="utf-8"
49 useDefaultWebProxy="true" allowCookies="false">
50 <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
51 maxBytesPerRead="4096" maxNameTableCharCount="16384" />
52 <reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false" />
53 <security mode="Message">
54 <transport clientCredentialType="Windows" proxyCredentialType="None"
55 realm="" />
56 <message clientCredentialType="Windows" negotiateServiceCredential="true"
57 algorithmSuite="Default" />
58 </security>
59 </binding>
60 </wsHttpBinding>
61 </bindings>
62 <client>
63 <endpoint address="http://localhost:7200/Example/ExampleService/"
64 binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IExampleService"
65 contract="ExampleServiceReference.IExampleService"
66 name="WSHttpBinding_IExampleService">
67 <identity>
68 <dns value="localhost" />
69 </identity>
70 </endpoint>
71 </client>
72 </system.serviceModel>
73 </configuration>

注意觀察運行結(jié)果,異步調(diào)用服務(wù)時,回調(diào)函數(shù)都是運行于CLR線程池的I/O線程當(dāng)中。



?

?

六、異步 SqlCommand

從ADO.NET 2.0開始,SqlCommand就新增了幾個異步方法執(zhí)行SQL命令。相對于同步執(zhí)行方式,它使主線程不需要等待數(shù)據(jù)庫的返回結(jié)果,在使用復(fù)雜性查詢或批量插入時將有效提高主線程的效率。使用異步SqlCommand的時候,請注意把ConnectionString 的?Asynchronous Processing 設(shè)置為 true 。

注意:SqlCommand異步操作的特別之處在于線程并不依賴于CLR線程池,而是由Windows內(nèi)部提供,這比使用異步委托更有效率。但如果需要使用回調(diào)函數(shù)的時候,回調(diào)函數(shù)的線程依然是來自于CLR線程池的工作者線程。

SqlCommand有以下幾個方法支持異步操作:

public IAsyncResult BeginExecuteNonQuery (......)
public int EndExecuteNonQuery(IAsyncResult)

public IAsyncResult BeginExecuteReader(......)
public SqlDataReader EndExecuteReader(IAsyncResult)

public IAsyncResult BeginExecuteXmlReader (......)
public XmlReader EndExecuteXmlReader(IAsyncResult)

?

由于使用方式相似,此處就以 BeginExecuteNonQuery 為例子,介紹一下異步SqlCommand的使用。首先建立connectionString,注意把Asynchronous Processing設(shè)置為true來啟動異步命令,然后把SqlCommand.CommandText設(shè)置為 WAITFOR DELAY "0:0:3" 來虛擬數(shù)據(jù)庫操作。再通過BeginExecuteNonQuery啟動異步操作,利用輪詢方式監(jiān)測操作情況。最后在操作完成后使用EndExecuteNonQuery完成異步操作。

1 class Program
2 {
3 //把Asynchronous Processing設(shè)置為true
4 static string connectionString = "Data Source=LESLIE-PC;Initial Catalog=Business;“+
5 "Integrated Security=True;Asynchronous Processing=true";
6
7 static void Main(string[] args)
8 {
9 //把CLR線程池最大線程數(shù)設(shè)置為1000
10 ThreadPool.SetMaxThreads(1000, 1000);
11 ThreadPoolMessage("Start");
12
13 //使用WAITFOR DELAY命令來虛擬操作
14 SqlConnection connection = new SqlConnection(connectionString);
15 SqlCommand command = new SqlCommand("WAITFOR DELAY '0:0:3';", connection);
16 connection.Open();
17
18 //啟動異步SqlCommand操作,利用輪詢方式監(jiān)測操作
19 IAsyncResult result = command.BeginExecuteNonQuery();
20 ThreadPoolMessage("BeginRead");
21 while (!result.AsyncWaitHandle.WaitOne(500))
22 Console.WriteLine("Main thread do work........");
23
24 //結(jié)束異步SqlCommand
25 int count= command.EndExecuteNonQuery(result);
26 ThreadPoolMessage("\nCompleted");
27 Console.ReadKey();
28 }
29
30 //顯示線程池現(xiàn)狀
31 static void ThreadPoolMessage(string data)
32 {
33 int a, b;
34 ThreadPool.GetAvailableThreads(out a, out b);
35 string message = string.Format("{0}\n CurrentThreadId is {1}\n "+
36 "WorkerThreads is:{2} CompletionPortThreads is :{3}\n",
37 data, Thread.CurrentThread.ManagedThreadId, a.ToString(), b.ToString());
38 Console.WriteLine(message);
39 }
40 }

注意運行結(jié)果,SqlCommand的異步執(zhí)行線程并不屬于CLR線程池。

?

如果覺得使用輪詢方式過于麻煩,可以使用回調(diào)函數(shù),但要注意當(dāng)調(diào)用回調(diào)函數(shù)時,線程是來自于CLR線程池的工作者線程。

1 class Program
2 {
3 //把Asynchronous Processing設(shè)置為true
4 static string connectionString = "Data Source=LESLIE-PC;Initial Catalog=Business;”+
5 “Integrated Security=True;Asynchronous Processing=true";
6 static void Main(string[] args)
7 {
8 //把CLR線程池最大線程數(shù)設(shè)置為1000
9 ThreadPool.SetMaxThreads(1000, 1000);
10 ThreadPoolMessage("Start");
11
12 //使用WAITFOR DELAY命令來虛擬操作
13 SqlConnection connection = new SqlConnection(connectionString);
14 SqlCommand command = new SqlCommand("WAITFOR DELAY '0:0:3';", connection);
15 connection.Open();
16
17 //啟動異步SqlCommand操作,并把SqlCommand對象傳遞到回調(diào)函數(shù)
18 IAsyncResult result = command.BeginExecuteNonQuery(
19 new AsyncCallback(AsyncCallbackMethod),command);
20 Console.ReadKey();
21 }
22
23 static void AsyncCallbackMethod(IAsyncResult result)
24 {
25 Thread.Sleep(200);
26 ThreadPoolMessage("AsyncCallback");
27 SqlCommand command = (SqlCommand)result.AsyncState;
28 int count=command.EndExecuteNonQuery(result);
29 command.Connection.Close();
30 }
31
32 //顯示線程池現(xiàn)狀
33 static void ThreadPoolMessage(string data)
34 {
35 int a, b;
36 ThreadPool.GetAvailableThreads(out a, out b);
37 string message = string.Format("{0}\n CurrentThreadId is {1}\n "+
38 "WorkerThreads is:{2} CompletionPortThreads is :{3}\n",
39 data, Thread.CurrentThread.ManagedThreadId, a.ToString(), b.ToString());
40
41 Console.WriteLine(message);
42 }
43 }

運行結(jié)果:

?

?

七、并行編程與PLINQ

要使用多線程開發(fā),必須非常熟悉Thread的使用,而且在開發(fā)過程中可能會面對很多未知的問題。為了簡化開發(fā),.NET 4.0 特別提供一個并行編程庫System.Threading.Tasks,它可以簡化并行開發(fā),你無需直接跟線程或線程池打交道,就可以簡單建立多線程應(yīng)用程序。此外,.NET還提供了新的一組擴展方法PLINQ,它具有自動分析查詢功能,如果并行查詢能提高系統(tǒng)效率,則同時運行,如果查詢未能從并行查詢中受益,則按原順序查詢。下面將詳細介紹并行操作的方式。

?

7.1 泛型委托

使用并行編程可以同時操作多個委托,在介紹并行編程前先簡單介紹一下兩個泛型委托System.Func<>與System.Action<>。

Func<>是一個能接受多個參數(shù)和一個返回值的泛型委托,它能接受0個到16個輸入?yún)?shù), 其中 T1,T2,T3,T4......T16 代表自定的輸入類型,TResult為自定義的返回值。
public delegate TResult Func<TResult>()
public delegate TResult Func<T1,TResult>(T1 arg1)
public delegate TResult Func<T1,T2, TResult>(T1 arg1,T2 arg2)
public delegate TResult Func<T1,T2, T3, TResult>(T1 arg1,T2 arg2,T3 arg3)
public delegate TResult Func<T1,T2, T3, ,T4, TResult>(T1 arg1,T2 arg2,T3 arg3,T4 arg4)
..............
public delegate TResult Func<T1,T2, T3, ,T4, ...... ,T16,TResult>(T1 arg1,T2 arg2,T3 arg3,T4 arg4,...... ,T16 arg16)

Action<>與Func<>十分相似,不同在于Action<>的返回值為void,Action能接受0~16個參數(shù)
public delegate void Action<T1>()
public delegate void Action<T1,T2>(T1 arg1,T2 arg2)
public delegate void Action<T1,T2, T3>(T1 arg1,T2 arg2, T3 arg3)
.............
public delegate void Action<T1,T2, T3, ,T4, ...... ,T16>(T1 arg1,T2 arg2,T3 arg3,T4 arg4,...... ,T16 arg16)

?

7.2 任務(wù)并行庫(TPL)

System.Threading.Tasks中的類被統(tǒng)稱為任務(wù)并行庫(Task Parallel Library,TPL),TPL使用CLR線程池把工作分配到CPU,并能自動處理工作分區(qū)、線程調(diào)度、取消支持、狀態(tài)管理以及其他低級別的細節(jié)操作,極大地簡化了多線程的開發(fā)。

注意:TPL比Thread更具智能性,當(dāng)它判斷任務(wù)集并沒有從并行運行中受益,就會選擇按順序運行。但并非所有的項目都適合使用并行開發(fā),創(chuàng)建過多并行任務(wù)可能會損害程序的性能,降低運行效率。

TPL包括常用的數(shù)據(jù)并行與任務(wù)并行兩種執(zhí)行方式:

7.2.1 數(shù)據(jù)并行

數(shù)據(jù)并行的核心類就是System.Threading.Tasks.Parallel,它包含兩個靜態(tài)方法 Parallel.For 與 Parallel.ForEach, 使用方式與for、foreach相仿。通過這兩個方法可以并行處理System.Func<>、System.Action<>委托。

以下一個例子就是利用 public static ParallelLoopResult For( int from, int max, Action<int>) 方法對List<Person>進行并行查詢。
假設(shè)使用單線程方式查詢3個Person對象,需要用時大約6秒,在使用并行方式,只需使用2秒就能完成查詢,而且能夠避開Thread的繁瑣處理。

1 class Program
2 {
3 static void Main(string[] args)
4 {
5 //設(shè)置最大線程數(shù)
6 ThreadPool.SetMaxThreads(1000, 1000);
7 //并行查詢
8 Parallel.For(0, 3,n =>
9 {
10 Thread.Sleep(2000); //模擬查詢
11 ThreadPoolMessage(GetPersonList()[n]);
12 });
13 Console.ReadKey();
14 }
15
16 //模擬源數(shù)據(jù)
17 static IList<Person> GetPersonList()
18 {
19 var personList = new List<Person>();
20
21 var person1 = new Person();
22 person1.ID = 1;
23 person1.Name = "Leslie";
24 person1.Age = 30;
25 personList.Add(person1);
26 ...........
27 return personList;
28 }
29
30 //顯示線程池現(xiàn)狀
31 static void ThreadPoolMessage(Person person)
32 {
33 int a, b;
34 ThreadPool.GetAvailableThreads(out a, out b);
35 string message = string.Format("Person ID:{0} Name:{1} Age:{2}\n" +
36 " CurrentThreadId is {3}\n WorkerThreads is:{4}" +
37 " CompletionPortThreads is :{5}\n",
38 person.ID, person.Name, person.Age,
39 Thread.CurrentThread.ManagedThreadId, a.ToString(), b.ToString());
40
41 Console.WriteLine(message);
42 }
43 }

觀察運行結(jié)果,對象并非按照原排列順序進行查詢,而是使用并行方式查詢。

?

若想停止操作,可以利用ParallelLoopState參數(shù),下面以ForEach作為例子。
public static ParallelLoopResult ForEach<TSource>( IEnumerable<TSource> source, Action<TSource, ParallelLoopState> action)
其中source為數(shù)據(jù)集,在Action<TSource,ParallelLoopState>委托的ParallelLoopState參數(shù)當(dāng)中包含有Break()和 Stop()兩個方法都可以使迭代停止。Break的使用跟傳統(tǒng)for里面的使用方式相似,但因為處于并行處理當(dāng)中,使用Break并不能保證所有運行能立即停止,在當(dāng)前迭代之前的迭代會繼續(xù)執(zhí)行。若想立即停止操作,可以使用Stop方法,它能保證立即終止所有的操作,無論它們是處于當(dāng)前迭代的之前還是之后。

1 class Program
2 {
3 static void Main(string[] args)
4 {
5 //設(shè)置最大線程數(shù)
6 ThreadPool.SetMaxThreads(1000, 1000);
7
8 //并行查詢
9 Parallel.ForEach(GetPersonList(), (person, state) =>
10 {
11 if (person.ID == 2)
12 state.Stop();
13 ThreadPoolMessage(person);
14 });
15 Console.ReadKey();
16 }
17
18 //模擬源數(shù)據(jù)
19 static IList<Person> GetPersonList()
20 {
21 var personList = new List<Person>();
22
23 var person1 = new Person();
24 person1.ID = 1;
25 person1.Name = "Leslie";
26 person1.Age = 30;
27 personList.Add(person1);
28 ..........
29 return personList;
30 }
31
32 //顯示線程池現(xiàn)狀
33 static void ThreadPoolMessage(Person person)
34 {
35 int a, b;
36 ThreadPool.GetAvailableThreads(out a, out b);
37 string message = string.Format("Person ID:{0} Name:{1} Age:{2}\n" +
38 " CurrentThreadId is {3}\n WorkerThreads is:{4}" +
39 " CompletionPortThreads is :{5}\n",
40 person.ID, person.Name, person.Age,
41 Thread.CurrentThread.ManagedThreadId, a.ToString(), b.ToString());
42
43 Console.WriteLine(message);
44 }
45 }

觀察運行結(jié)果,當(dāng)Person的ID等于2時,運行將會停止。

?

當(dāng)要在多個線程中調(diào)用本地變量,可以使用以下方法:
public static ParallelLoopResult ForEach<TSource, TLocal>(IEnumerable<Of TSource>, Func<Of TLocal>, Func<Of TSource,ParallelLoopState,TLocal,TLocal>, Action<Of TLocal>)
其中第一個參數(shù)為數(shù)據(jù)集;
第二個參數(shù)是一個Func委托,用于在每個線程執(zhí)行前進行初始化;
第 三個參數(shù)是委托Func<Of T1,T2,T3,TResult>,它能對數(shù)據(jù)集的每個成員進行迭代,當(dāng)中T1是數(shù)據(jù)集的成員,T2是一個ParallelLoopState對 象,它可以控制迭代的狀態(tài),T3是線程中的本地變量;
第四個參數(shù)是一個Action委托,用于對每個線程的最終狀態(tài)進行最終操作。

在以下例子中,使用ForEach計算多個Order的總體價格。在ForEach方法中,首先把參數(shù)初始化為0f,然后用把同一個Order的多個OrderItem價格進行累加,計算出Order的價格,最后把多個Order的價格進行累加,計算出多個Order的總體價格。

1 public class Order
2 {
3 public int ID;
4 public float Price;
5 }
6
7 public class OrderItem
8 {
9 public int ID;
10 public string Goods;
11 public int OrderID;
12 public float Price;
13 public int Count;
14 }
15
16 class Program
17 {
18 static void Main(string[] args)
19 {
20 //設(shè)置最大線程數(shù)
21 ThreadPool.SetMaxThreads(1000, 1000);
22 float totalPrice = 0f;
23 //并行查詢
24 var parallelResult = Parallel.ForEach(GetOrderList(),
25 () => 0f, //把參數(shù)初始值設(shè)為0
26 (order, state, orderPrice) =>
27 {
28 //計算單個Order的價格
29 orderPrice = GetOrderItem().Where(item => item.OrderID == order.ID)
30 .Sum(item => item.Price * item.Count);
31 order.Price = orderPrice;
32 ThreadPoolMessage(order);
33
34 return orderPrice;
35 },
36 (finallyPrice) =>
37 {
38 totalPrice += finallyPrice;//計算多個Order的總體價格
39 }
40 );
41
42 while (!parallelResult.IsCompleted)
43 Console.WriteLine("Doing Work!");
44
45 Console.WriteLine("Total Price is:" + totalPrice);
46 Console.ReadKey();
47 }
48 //虛擬數(shù)據(jù)
49 static IList<Order> GetOrderList()
50 {
51 IList<Order> orderList = new List<Order>();
52 Order order1 = new Order();
53 order1.ID = 1;
54 orderList.Add(order1);
55 ............
56 return orderList;
57 }
58 //虛擬數(shù)據(jù)
59 static IList<OrderItem> GetOrderItem()
60 {
61 IList<OrderItem> itemList = new List<OrderItem>();
62
63 OrderItem orderItem1 = new OrderItem();
64 orderItem1.ID = 1;
65 orderItem1.Goods = "iPhone 4S";
66 orderItem1.Price = 6700;
67 orderItem1.Count = 2;
68 orderItem1.OrderID = 1;
69 itemList.Add(orderItem1);
70 ...........
71 return itemList;
72 }
73
74 //顯示線程池現(xiàn)狀
75 static void ThreadPoolMessage(Order order)
76 {
77 int a, b;
78 ThreadPool.GetAvailableThreads(out a, out b);
79 string message = string.Format("OrderID:{0} OrderPrice:{1}\n" +
80 " CurrentThreadId is {2}\n WorkerThreads is:{3}" +
81 " CompletionPortThreads is:{4}\n",
82 order.ID, order.Price,
83 Thread.CurrentThread.ManagedThreadId, a.ToString(), b.ToString());
84
85 Console.WriteLine(message);
86 }
87 }

運行結(jié)果

?

?7.2.2 任務(wù)并行

在TPL當(dāng)中還可以使用Parallel.Invoke方法觸發(fā)多個異步任務(wù),其中 actions 中可以包含多個方法或者委托,parallelOptions用于配置Parallel類的操作。
public static void Invoke(Action[] actions )
public static void Invoke(ParallelOptions parallelOptions, Action[] actions )
下面例子中利用了Parallet.Invoke并行查詢多個Person,actions當(dāng)中可以綁定方法、lambda表達式或者委托,注意綁定方法時必須是返回值為void的無參數(shù)方法。

1 class Program
2 {
3 static void Main(string[] args)
4 {
5 //設(shè)置最大線程數(shù)
6 ThreadPool.SetMaxThreads(1000, 1000);
7
8 //任務(wù)并行
9 Parallel.Invoke(option,
10 PersonMessage,
11 ()=>ThreadPoolMessage(GetPersonList()[1]),
12 delegate(){
13 ThreadPoolMessage(GetPersonList()[2]);
14 });
15 Console.ReadKey();
16 }
17
18 static void PersonMessage()
19 {
20 ThreadPoolMessage(GetPersonList()[0]);
21 }
22
23 //顯示線程池現(xiàn)狀
24 static void ThreadPoolMessage(Person person)
25 {
26 int a, b;
27 ThreadPool.GetAvailableThreads(out a, out b);
28 string message = string.Format("Person ID:{0} Name:{1} Age:{2}\n" +
29 " CurrentThreadId is {3}\n WorkerThreads is:{4}" +
30 " CompletionPortThreads is :{5}\n",
31 person.ID, person.Name, person.Age,
32 Thread.CurrentThread.ManagedThreadId, a.ToString(), b.ToString());
33
34 Console.WriteLine(message);
35 }
36
37 //模擬源數(shù)據(jù)
38 static IList<Person> GetPersonList()
39 {
40 var personList = new List<Person>();
41
42 var person1 = new Person();
43 person1.ID = 1;
44 person1.Name = "Leslie";
45 person1.Age = 30;
46 personList.Add(person1);
47 ..........
48 return personList;
49 }
50 }

運行結(jié)果

?

?

7.3 Task簡介

以Thread創(chuàng)建的線程被默認為前臺線程,當(dāng)然你可以把線程IsBackground屬性設(shè)置為true,但TPL為此提供了一個更簡單的類Task。
Task存在于System.Threading.Tasks命名空間當(dāng)中,它可以作為異步委托的簡單替代品。
通過Task的Factory屬性將返回TaskFactory類,以TaskFactory.StartNew(Action)方法可以創(chuàng)建一個新線程,所創(chuàng)建的線程默認為后臺線程。

1 class Program
2 {
3 static void Main(string[] args)
4 {
5 ThreadPool.SetMaxThreads(1000, 1000);
6 Task.Factory.StartNew(() => ThreadPoolMessage());
7 Console.ReadKey();
8 }
9
10 //顯示線程池現(xiàn)狀
11 static void ThreadPoolMessage()
12 {
13 int a, b;
14 ThreadPool.GetAvailableThreads(out a, out b);
15 string message = string.Format("CurrentThreadId is:{0}\n" +
16 "CurrentThread IsBackground:{1}\n" +
17 "WorkerThreads is:{2}\nCompletionPortThreads is:{3}\n",
18 Thread.CurrentThread.ManagedThreadId,
19 Thread.CurrentThread.IsBackground.ToString(),
20 a.ToString(), b.ToString());
21 Console.WriteLine(message);
22 }
23 }

運行結(jié)果

?

?

若要取消處理,可以利用CancellationTakenSource對象,在TaskFactory中包含有方法
public Task StartNew( Action action, CancellationToken cancellationToken )
在方法中加入CancellationTakenSource對象的CancellationToken屬性,可以控制任務(wù)的運行,調(diào)用CancellationTakenSource.Cancel時任務(wù)就會自動停止。下面以圖片下載為例子介紹一下TaskFactory的使用。

服務(wù)器端頁面

1 <html xmlns="http://www.w3.org/1999/xhtml">
2 <head runat="server">
3 <title></title>
4 <script type="text/C#" runat="server">
5 private static List<string> url=new List<string>();
6
7 protected void Page_Load(object sender, EventArgs e)
8 {
9 if (!Page.IsPostBack)
10 {
11 url.Clear();
12 Application["Url"] = null;
13 }
14 }
15
16 protected void CheckBox_CheckedChanged(object sender, EventArgs e)
17 {
18 CheckBox checkBox = (CheckBox)sender;
19 if (checkBox.Checked)
20 url.Add(checkBox.Text);
21 else
22 url.Remove(checkBox.Text);
23 Application["Url"]= url;
24 }
25 </script>
26 </head>
27 <body>
28 <form id="form1" runat="server" >
29 <div align="left">
30 <div align="center" style="float: left;">
31 <asp:Image ID="Image1" runat="server" ImageUrl="~/Images/A.jpg" /><br />
32 <asp:CheckBox ID="CheckBox1" runat="server" AutoPostBack="True"
33 oncheckedchanged="CheckBox_CheckedChanged" Text="A.jpg" />
34 </div>
35 <div align="center" style="float: left">
36 <asp:Image ID="Image2" runat="server" ImageUrl="~/Images/B.jpg" /><br />
37 <asp:CheckBox ID="CheckBox2" runat="server" AutoPostBack="True"
38 oncheckedchanged="CheckBox_CheckedChanged" Text="B.jpg" />
39 </div>
40 <div align="center" style="float: left">
41 <asp:Image ID="Image3" runat="server" ImageUrl="~/Images/C.jpg" /><br />
42 <asp:CheckBox ID="CheckBox3" runat="server" AutoPostBack="True"
43 oncheckedchanged="CheckBox_CheckedChanged" Text="C.jpg" />
44 </div>
45 <div align="center" style="float: left">
46 <asp:Image ID="Image4" runat="server" ImageUrl="~/Images/D.jpg" /><br />
47 <asp:CheckBox ID="CheckBox4" runat="server" AutoPostBack="True"
48 oncheckedchanged="CheckBox_CheckedChanged" Text="D.jpg" />
49 </div>
50 <div align="center" style="float: left">
51 <asp:Image ID="Image5" runat="server" ImageUrl="~/Images/E.jpg" /><br />
52 <asp:CheckBox ID="CheckBox5" runat="server" AutoPostBack="True"
53 oncheckedchanged="CheckBox_CheckedChanged" Text="E.jpg" />
54 </div>
55 </div>
56 </form>
57 </body>
58 </html>

首先在服務(wù)器頁面中顯示多個*.jpg圖片,每個圖片都有對應(yīng)的CheckBox檢測其選擇情況。
所選擇圖片的路徑會記錄在Application["Url"]當(dāng)中傳遞到Handler.ashx當(dāng)中。

注意:Application是一個全局變量,此處只是為了顯示Task的使用方式,在ASP.NET開發(fā)應(yīng)該慎用Application。

Handler.ashx 處理圖片的下載,它從 Application["Url"] 當(dāng)中獲取所選擇圖片的路徑,并把圖片轉(zhuǎn)化成byte[]二進制數(shù)據(jù)。
再把圖片的數(shù)量,每副圖片的二進制數(shù)據(jù)的長度記錄在OutputStream的頭部。
最后把圖片的二進制數(shù)據(jù)記入 OutputStream 一并輸出。

1 public class Handler : IHttpHandler
2 {
3 public void ProcessRequest(HttpContext context)
4 {
5 //獲取圖片名,把圖片數(shù)量寫OutputStream
6 List<String> urlList = (List<string>)context.Application["Url"];
7 context.Response.OutputStream.Write(BitConverter.GetBytes(urlList.Count), 0, 4);
8
9 //把圖片轉(zhuǎn)換成二進制數(shù)據(jù)
10 List<string> imageList = GetImages(urlList);
11
12 //把每副圖片長度寫入OutputStream
13 foreach (string image in imageList)
14 {
15 byte[] imageByte=Convert.FromBase64String(image);
16 context.Response.OutputStream.Write(BitConverter.GetBytes(imageByte.Length),0,4);
17 }
18
19 //把圖片寫入OutputStream
20 foreach (string image in imageList)
21 {
22 byte[] imageByte = Convert.FromBase64String(image);
23 context.Response.OutputStream.Write(imageByte,0,imageByte.Length);
24 }
25 }
26
27 //獲取多個圖片的二進制數(shù)據(jù)
28 private List<string> GetImages(List<string> urlList)
29 {
30 List<string> imageList = new List<string>();
31 foreach (string url in urlList)
32 imageList.Add(GetImage(url));
33 return imageList;
34 }
35
36 //獲取單副圖片的二進制數(shù)據(jù)
37 private string GetImage(string url)
38 {
39 string path = "E:/My Projects/Example/WebSite/Images/"+url;
40 FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read);
41 byte[] imgBytes = new byte[10240];
42 int imgLength = stream.Read(imgBytes, 0, 10240);
43 return Convert.ToBase64String(imgBytes,0,imgLength);
44 }
45
46 public bool IsReusable
47 {
48 get{ return false;}
49 }
50 }

?

客戶端

建立一個WinForm窗口,里面加入一個WebBrowser連接到服務(wù)器端的Default.aspx頁面。
當(dāng)按下Download按鍵時,系統(tǒng)就會利用TaskFactory.StartNew的方法建立異步線程,使用WebRequest方法向Handler.ashx發(fā)送請求。
接收到回傳流時,就會根據(jù)頭文件的內(nèi)容判斷圖片的數(shù)量與每副圖片的長度,把二進制數(shù)據(jù)轉(zhuǎn)化為*.jpg文件保存。

系統(tǒng)利用TaskFactory.StartNew(action,cancellationToken) 方式異步調(diào)用GetImages方法進行圖片下載。?
當(dāng)用戶按下Cancel按鈕時,異步任務(wù)就會停止。值得注意的是,在圖片下載時調(diào)用了CancellationToken.ThrowIfCancellationRequested方法,目的在檢查并行任務(wù)的運行情況,在并行任務(wù)被停止時釋放出OperationCanceledException異常,確保用戶按下Cancel按鈕時,停止所有并行任務(wù)。

1 public partial class Form1 : Form
2 {
3 private CancellationTokenSource tokenSource = new CancellationTokenSource();
4
5 public Form1()
6 {
7 InitializeComponent();
8 ThreadPool.SetMaxThreads(1000, 1000);
9 }
10
11 private void downloadToolStripMenuItem_Click(object sender, EventArgs e)
12 {
13 Task.Factory.StartNew(GetImages,tokenSource.Token);
14 }
15
16 private void cancelToolStripMenuItem_Click(object sender, EventArgs e)
17 {
18 tokenSource.Cancel();
19 }
20
21 private void GetImages()
22 {
23 //發(fā)送請求,獲取輸出流
24 WebRequest webRequest = HttpWebRequest.Create("Http://localhost:5800/Handler.ashx");
25 Stream responseStream=webRequest.GetResponse().GetResponseStream();
26
27 byte[] responseByte = new byte[81960];
28 IAsyncResult result=responseStream.BeginRead(responseByte,0,81960,null,null);
29 int responseLength = responseStream.EndRead(result);
30
31 //獲取圖片數(shù)量
32 int imageCount = BitConverter.ToInt32(responseByte, 0);
33
34 //獲取每副圖片的長度
35 int[] lengths = new int[imageCount];
36 for (int n = 0; n < imageCount; n++)
37 {
38 int length = BitConverter.ToInt32(responseByte, (n + 1) * 4);
39 lengths[n] = length;
40 }
41 try
42 {
43 //保存圖片
44 for (int n = 0; n < imageCount; n++)
45 {
46 string path = string.Format("E:/My Projects/Example/Test/Images/pic{0}.jpg", n);
47 FileStream file = new FileStream(path, FileMode.Create, FileAccess.ReadWrite);
48
49 //計算字節(jié)偏移量
50 int offset = (imageCount + 1) * 4;
51 for (int a = 0; a < n; a++)
52 offset += lengths[a];
53
54 file.Write(responseByte, offset, lengths[n]);
55 file.Flush();
56
57 //模擬操作
58 Thread.Sleep(1000);
59
60 //檢測CancellationToken變化
61 tokenSource.Token.ThrowIfCancellationRequested();
62 }
63 }
64 catch (OperationCanceledException ex)
65 {
66 MessageBox.Show("Download cancel!");
67 }
68 }
69 }



7.4 并行查詢(PLINQ)

并行 LINQ (PLINQ) 是 LINQ 模式的并行實現(xiàn),主要區(qū)別在于 PLINQ 嘗試充分利用系統(tǒng)中的所有處理器。?它利用所有處理器的方法,把數(shù)據(jù)源分成片段,然后在多個處理器上對單獨工作線程上的每個片段并行執(zhí)行查詢,?在許多情況下,并行執(zhí)行意味著查詢運行速度顯著提高。但這并不說明所有PLINQ都會使用并行方式,當(dāng)系統(tǒng)測試要并行查詢會對系統(tǒng)性能造成損害時,那將自動化地使用同步執(zhí)行。
在System.Linq.ParallelEnumerable類中,包含了并行查詢的大部分方法。
?

方法成員 

說明

AsParallel

PLINQ 的入口點。?指定如果可能,應(yīng)并行化查詢的其余部分。

AsSequential(Of?TSource)

指定查詢的其余部分應(yīng)像非并行 LINQ 查詢一樣按順序運行。

AsOrdered

指定 PLINQ 應(yīng)保留查詢的其余部分的源序列排序,直到例如通過使用 orderby(在 Visual Basic 中為 Order By)子句更改排序為止。

AsUnordered(Of?TSource)

指定查詢的其余部分的 PLINQ 不需要保留源序列的排序。

WithCancellation(Of?TSource)

指定 PLINQ 應(yīng)定期監(jiān)視請求取消時提供的取消標(biāo)記和取消執(zhí)行的狀態(tài)。

WithDegreeOfParallelism(Of?TSource)

指定 PLINQ 應(yīng)當(dāng)用來并行化查詢的處理器的最大數(shù)目。

WithMergeOptions(Of?TSource)

提供有關(guān) PLINQ 應(yīng)當(dāng)如何(如果可能)將并行結(jié)果合并回到使用線程上的一個序列的提示。

WithExecutionMode(Of?TSource)

指定 PLINQ 應(yīng)當(dāng)如何并行化查詢(即使默認行為是按順序運行查詢)。

ForAll(Of?TSource)

多線程枚舉方法,與循環(huán)訪問查詢結(jié)果不同,它允許在不首先合并回到使用者線程的情況下并行處理結(jié)果。

Aggregate?重載

對于 PLINQ 唯一的重載,它啟用對線程本地分區(qū)的中間聚合以及一個用于合并所有分區(qū)結(jié)果的最終聚合函數(shù)。

?

7.4.1 AsParallel

通常想要實現(xiàn)并行查詢,只需向數(shù)據(jù)源添加?AsParallel?查詢操作即可。

1 class Program
2 {
3 static void Main(string[] args)
4 {
5 var personList=GetPersonList().AsParallel()
6 .Where(x=>x.Age>30);
7 Console.ReadKey();
8 }
9
10 //模擬源數(shù)據(jù)
11 static IList<Person> GetPersonList()
12 {
13 var personList = new List<Person>();
14
15 var person1 = new Person();
16 person1.ID = 1;
17 person1.Name = "Leslie";
18 person1.Age = 30;
19 personList.Add(person1);
20 ...........
21 return personList;
22 }
23 }

?

7.4.2 AsOrdered

若要使查詢結(jié)果必須保留源序列排序方式,可以使用AsOrdered方法。?
AsOrdered依然使用并行方式,只是在查詢過程加入額外信息,在并行結(jié)束后把查詢結(jié)果再次進行排列。

1 class Program
2 {
3 static void Main(string[] args)
4 {
5 var personList=GetPersonList().AsParallel().AsOrdered()
6 .Where(x=>x.Age<30);
7 Console.ReadKey();
8 }
9
10 static IList<Person> GetPersonList()
11 {......}
12 }


7.4.3 WithDegreeOfParallelism

默認情況下,PLINQ 使用主機上的所有處理器,這些處理器的數(shù)量最多可達 64 個。
通過使用 WithDegreeOfParallelism(Of TSource) 方法,可以指示 PLINQ 使用不多于指定數(shù)量的處理器。

1 class Program
2 {
3 static void Main(string[] args)
4 {
5 var personList=GetPersonList().AsParallel().WithDegreeOfParallelism(2)
6 .Where(x=>x.Age<30);
7 Console.ReadKey();
8 }
9
10 static IList<Person> GetPersonList()
11 {.........}
12 }

?

7.4.4 ForAll

如果要對并行查詢結(jié)果進行操作,一般會在for或foreach中執(zhí)行,執(zhí)行枚舉操作時會使用同步方式。
有見及此,PLINQ中包含了ForAll方法,它可以使用并行方式對數(shù)據(jù)集進行操作。

1 class Program
2 {
3 static void Main(string[] args)
4 {
5 ThreadPool.SetMaxThreads(1000, 1000);
6 GetPersonList().AsParallel().ForAll(person =>{
7 ThreadPoolMessage(person);
8 });
9 Console.ReadKey();
10 }
11
12 static IList<Person> GetPersonList()
13 {.......}
14
15 //顯示線程池現(xiàn)狀
16 static void ThreadPoolMessage(Person person)
17 {
18 int a, b;
19 ThreadPool.GetAvailableThreads(out a, out b);
20 string message = string.Format("Person ID:{0} Name:{1} Age:{2}\n" +
21 " CurrentThreadId is {3}\n WorkerThreads is:{4}" +
22 " CompletionPortThreads is :{5}\n",
23 person.ID, person.Name, person.Age,
24 Thread.CurrentThread.ManagedThreadId, a.ToString(), b.ToString());
25 Console.WriteLine(message);
26 }
27 }

運行結(jié)果

?

7.4.5 WithCancellation

如果需要停止查詢,可以使用 WithCancellation(Of TSource) 運算符并提供 CancellationToken 實例作為參數(shù)。?
與第三節(jié)Task的例子相似,如果標(biāo)記上的 IsCancellationRequested 屬性設(shè)置為 true,則 PLINQ 將會注意到它,并停止所有線程上的處理,然后引發(fā) OperationCanceledException。這可以保證并行查詢能夠立即停止。

1 class Program
2 {
3 static CancellationTokenSource tokenSource = new CancellationTokenSource();
4
5 static void Main(string[] args)
6 {
7 Task.Factory.StartNew(Cancel);
8 try
9 {
10 GetPersonList().AsParallel().WithCancellation(tokenSource.Token)
11 .ForAll(person =>
12 {
13 ThreadPoolMessage(person);
14 });
15 }
16 catch (OperationCanceledException ex)
17 { }
18 Console.ReadKey();
19 }
20
21 //在10~50毫秒內(nèi)發(fā)出停止信號
22 static void Cancel()
23 {
24 Random random = new Random();
25 Thread.Sleep(random.Next(10,50));
26 tokenSource.Cancel();
27 }
28
29 static IList<Person> GetPersonList()
30 {......}
31
32 //顯示線程池現(xiàn)狀
33 static void ThreadPoolMessage(Person person)
34 {
35 int a, b;
36 ThreadPool.GetAvailableThreads(out a, out b);
37 string message = string.Format("Person ID:{0} Name:{1} Age:{2}\n" +
38 " CurrentThreadId is {3}\n WorkerThreads is:{4}" +
39 " CompletionPortThreads is :{5}\n",
40 person.ID, person.Name, person.Age,
41 Thread.CurrentThread.ManagedThreadId, a.ToString(), b.ToString());
42 Console.WriteLine(message);
43 }
44 }
45

?

八、定時器與鎖

8.1定時器

若要長期定時進行一些工作,比如像郵箱更新,實時收聽信息等等,可以利用定時器Timer進行操作。
在System.Threading命名空間中存在Timer類與對應(yīng)的TimerCallback委托,它可以在后臺線程中執(zhí)行一些長期的定時操作,使主線程不受干擾。
Timer類中最常用的構(gòu)造函數(shù)為 public Timer( timerCallback , object , int , int )
timerCallback委托可以綁定執(zhí)行方法,執(zhí)行方法必須返回void,它可以是無參數(shù)方法,也可以帶一個object參數(shù)的方法。
第二個參數(shù)是為 timerCallback 委托輸入的參數(shù)對象。
第三個參數(shù)是開始執(zhí)行前等待的時間。
第四個參數(shù)是每次執(zhí)行之間的等待時間。

開發(fā)實例

1 class Program
2 {
3 static void Main(string[] args)
4 {
5 ThreadPool.SetMaxThreads(1000, 1000);
6
7 TimerCallback callback = new TimerCallback(ThreadPoolMessage);
8 Timer t = new Timer(callback,"Hello Jack! ", 0, 1000);
9 Console.ReadKey();
10 }
11
12 //顯示線程池現(xiàn)狀
13 static void ThreadPoolMessage(object data)
14 {
15 int a, b;
16 ThreadPool.GetAvailableThreads(out a, out b);
17 string message = string.Format("{0}\n CurrentThreadId is:{1}\n" +
18 " CurrentThread IsBackground:{2}\n" +
19 " WorkerThreads is:{3}\n CompletionPortThreads is:{4}\n",
20 data + "Time now is " + DateTime.Now.ToLongTimeString(),
21 Thread.CurrentThread.ManagedThreadId,
22 Thread.CurrentThread.IsBackground.ToString(),
23 a.ToString(), b.ToString());
24 Console.WriteLine(message);
25 }
26 }

注意觀察運行結(jié)果,每次調(diào)用Timer綁定的方法時不一定是使用同一線程,但線程都會是來自工作者線程的后臺線程。


8.2 鎖

在使用多線程開發(fā)時,存在一定的共用數(shù)據(jù),為了避免多線程同時操作同一數(shù)據(jù),.NET提供了lock、Monitor、Interlocked等多個鎖定數(shù)據(jù)的方式。

8.2.1 lock

lock的使用比較簡單,如果需要鎖定某個對象時,可以直接使用lock(this)的方式。

1 private void Method()
2 {
3 lock(this)
4 {
5 //在此進行的操作能保證在同一時間內(nèi)只有一個線程對此對象操作
6 }
7 }

如果操作只鎖定某段代碼,可以事先建立一個object對象,并對此對象進行操作鎖定,這也是.net提倡的鎖定用法。

1 class Control
2 {
3 private object obj=new object();
4
5 public void Method()
6 {
7 lock(obj)
8 {.......}
9 }
10 }

?

8.2.2 Montior

Montior存在于System.Thread命名空間內(nèi),相比lock,Montior使用更靈活。
它存在 Enter, Exit 兩個方法,它可以對對象進行鎖定與解鎖,比lock使用更靈活。

1 class Control
2 {
3 private object obj=new object();
4
5 public void Method()
6 {
7 Monitor.Enter(obj);
8 try
9 {......}
10 catch(Excetion ex)
11 {......}
12 finally
13 {
14 Monitor.Exit(obj);
15 }
16 }
17 }
18

使用try的方式,能確保程序不會因死鎖而釋放出異常!
而且在finally中釋放obj對象能夠確保無論是否出現(xiàn)死鎖狀態(tài),系統(tǒng)都會釋放obj對象。
而且Monitor中還存在Wait方法可以讓線程等待一段時間,然后在完成時使用Pulse、PulseAll等方法通知等待線程。

?

8.2.3 Interlocked

Interlocked存在于System.Thread命名空間內(nèi),它的操作比Monitor使用更簡單。
它存在CompareExchange、Decrement、Exchange、Increment等常用方法讓參數(shù)在安全的情況進行數(shù)據(jù)交換。

Increment、Decrement 可以使參數(shù)安全地加1或減1并返回遞增后的新值。

1 class Example
2 {
3 private int a=1;
4
5 public void AddOne()
6 {
7 int newA=Interlocked.Increment(ref a);
8 }
9 }

Exchange可以安全地變量賦值。

1 public void SetData()
2 {
3 Interlocked.Exchange(ref a,100);
4 }

CompareExchange使用特別方便,它相當(dāng)于if的用法,當(dāng)a等于1時,則把100賦值給a。

1 public void CompareAndExchange()
2 {
3 Interlocked.CompareExchange(ref a,100,1);
4 }

?

轉(zhuǎn)載于:https://www.cnblogs.com/lvjy-net/p/8510171.html

總結(jié)

以上是生活随笔為你收集整理的C#细说多线程(下)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

亚洲午夜精品久久久久久久久久久久 | 欧美一区二区伦理片 | av电影中文字幕 | 亚洲成人精品久久久 | 亚洲精品乱码久久久一二三 | 免费视频97 | 国产精品第一页在线观看 | 天天弄天天干 | 精品视频成人 | 美女国产| 免费黄色网址大全 | 国产精品一区二区免费视频 | 欧美日韩中文字幕综合视频 | 婷婷色站 | 久久久福利视频 | 久草在线国产 | 亚洲午夜av | 成年人免费在线观看 | 欧洲精品亚洲精品 | 91人人视频在线观看 | 93久久精品日日躁夜夜躁欧美 | 91亚洲精品乱码久久久久久蜜桃 | 久艹视频在线观看 | 亚洲成年人在线播放 | 亚洲成色777777在线观看影院 | 久久大香线蕉app | 2018好看的中文在线观看 | 精品国产一二三四区 | 日韩色在线观看 | 96精品视频 | 91精品啪在线观看国产线免费 | 深夜免费福利网站 | 日韩国产精品久久久久久亚洲 | 日韩激情在线 | 狠狠狠狠狠狠干 | 中文字幕在线观看的网站 | 久久久久久久久综合 | 91av福利视频 | 韩国av免费在线观看 | 特级西西人体444是什么意思 | 亚洲精品国产免费 | 五月天色中色 | 激情视频免费在线 | 日日碰狠狠躁久久躁综合网 | 成人国产精品久久久久久亚洲 | 午夜视频亚洲 | 久久久久综合视频 | 99久免费精品视频在线观看 | 97国产精品 | 91视频麻豆视频 | 国产一级视频 | 婷婷国产一区二区三区 | 国产视频一区二区三区在线 | 日韩理论片在线观看 | 久久99国产精品自在自在app | 在线免费观看av网站 | 在线国产不卡 | 国产尤物一区二区三区 | 亚洲黄a | 免费男女羞羞的视频网站中文字幕 | 日韩高清无线码2023 | 日韩欧美xxx| 国产高清免费在线播放 | 天天操天天操天天 | 欧美日韩后 | 国产精品黄色 | 日韩久久影院 | 手机av片 | 色综合天天狠天天透天天伊人 | 久久资源在线 | 亚洲日日夜夜 | 日韩高清观看 | 欧美精品一区二区三区四区在线 | av资源免费在线观看 | 中文字幕日韩一区二区三区不卡 | 曰韩在线 | 精品久久久免费视频 | 精品亚洲视频在线 | 最近高清中文在线字幕在线观看 | 91精品影视 | 亚洲性xxxx| 99久久精品国产观看 | 精品欧美一区二区精品久久 | 在线日韩中文 | 久99久视频 | 激情婷婷在线观看 | 午夜av激情 | 天天看天天操 | 日本成人中文字幕在线观看 | 精品国产乱码久久久久久浪潮 | 在线观看国产福利片 | 在线观看一区二区精品 | 四虎永久精品在线 | 日韩av看片| 亚洲综合色视频 | 国产精品自产拍在线观看蜜 | 亚洲成年人免费网站 | 亚洲黄色app | 国产色综合 | 欧美在线1 | 日韩视频中文字幕 | 久久精品理论 | 国产资源精品在线观看 | 日韩在线观看精品 | 国产精品一级视频 | 国产亚洲精品久久久久秋 | 在线欧美中文字幕 | 国产99久久99热这里精品5 | 国产精品视频不卡 | 欧美精品xxx | 日韩高清黄色 | 国产黄色片在线免费观看 | 日韩av片免费在线观看 | 久久综合五月天婷婷伊人 | 国产伦理一区二区三区 | 最近字幕在线观看第一季 | 久草在在线 | 国产成人久久 | 国产做a爱一级久久 | 成 人 黄 色 视频播放1 | 中文在线www | 欧美成人一二区 | 四虎国产永久在线精品 | 91免费看黄色 | 亚洲国产天堂av | 97偷拍在线视频 | 久久综合五月婷婷 | 最近日韩免费视频 | 特级黄色视频毛片 | 久久黄色影院 | avlulu久久精品 | 日韩特级毛片 | 人人超碰在线 | 亚洲欧美视频 | 精品久久美女 | 成人免费在线看片 | av久久久 | 亚洲成人免费在线 | 久久99欧美| 精品福利在线 | 一本—道久久a久久精品蜜桃 | 日韩欧美在线第一页 | 尤物97国产精品久久精品国产 | 免费亚洲精品 | 国产一级二级在线观看 | 亚洲精品视频网址 | 二区三区在线观看 | 91日韩在线专区 | 免费看91的网站 | 国产一级二级三级视频 | 欧美做受高潮电影o | 国产成人三级三级三级97 | 超碰97国产在线 | 国产精品伦一区二区三区视频 | 亚洲aⅴ久久精品 | 黄p在线播放 | 欧美日韩在线视频观看 | 蜜臀91丨九色丨蝌蚪老版 | 97视频在线 | 成人免费观看av | 国产精品区二区三区日本 | 色综合色综合久久综合频道88 | av一区二区三区在线 | 99精品国产aⅴ | 亚洲视频精品在线 | av在线免费播放网站 | 在线免费观看黄色 | 精品国产伦一区二区三区观看方式 | 天天色天天射天天综合网 | 99在线视频观看 | 中文av字幕在线观看 | 国产99色 | 国产在线精品国自产拍影院 | 在线天堂亚洲 | 婷婷六月色 | 中文字幕av免费在线观看 | 天天射天天操天天干 | 色99久久| 日韩精品无码一区二区三区 | 久久国产精品99精国产 | 日韩免费观看av | 欧美一区二区伦理片 | 色丁香色婷婷 | 狠狠88综合久久久久综合网 | av最新资源 | 日韩1级片 | 日韩中文字幕在线不卡 | 亚洲做受高潮欧美裸体 | av不卡中文字幕 | 中文亚洲欧美日韩 | 国产午夜精品一区二区三区欧美 | 久久成人精品视频 | 日韩免费一级电影 | 国产精品正在播放 | 黄色毛片一级 | 中文字幕av在线电影 | 久久深爱网 | 三级黄色片在线观看 | 午夜av在线 | 日日干天天操 | 视频1区2区 | 久久歪歪 | 六月丁香婷婷久久 | 亚洲成a人片在线观看网站口工 | 黄色av影院| 麻豆视频免费观看 | 91大片网站 | 99热这里只有精品国产首页 | 国产中文 | 天天干天天爽 | 成片免费观看视频 | 91av在线国产 | 亚洲少妇久久 | 狠狠色丁香 | av免费看av | 波多野结依在线观看 | 欧美a在线免费观看 | 9999毛片| 精品一二三四视频 | 激情婷婷在线观看 | 亚洲乱亚洲乱亚洲 | 国产一区二区在线看 | www.888.av| a视频在线观看 | 成人一区二区三区在线观看 | 人人超在线公开视频 | 黄色在线成人 | 一区二区精品视频 | 麻豆91在线 | 久久香蕉国产 | 日日摸日日添夜夜爽97 | 超碰精品在线 | 亚洲国产偷 | 国产精品99在线播放 | 黄色小网站在线 | 欧美日韩在线免费视频 | 亚洲精品在线观看网站 | 国产精品色婷婷 | 国产一区二区久久 | 超碰九九 | 特级a老妇做爰全过程 | 人人爽人人爽人人爽人人爽 | 四虎在线免费视频 | 中文字幕在线中文 | 91免费看黄色 | 蜜臀aⅴ国产精品久久久国产 | 日本成人黄色片 | 国产精品久久久影视 | 视频在线精品 | 91视频免费观看 | 久久久精品久久 | 热精品 | 久久在线免费观看 | 久久高清视频免费 | 四虎成人av| 日韩av电影中文字幕 | 午夜精品一二区 | 亚洲欧洲日韩在线观看 | 中文字幕第一 | 久久久久久久久久久影视 | 久久免费的精品国产v∧ | 精品国内自产拍在线观看视频 | 欧美日韩精品在线观看视频 | 天天射天天做 | 国产精品视频内 | 久久亚洲私人国产精品 | 在线观看免费版高清版 | 91亚洲激情| 欧美不卡在线 | 日韩欧美精品在线 | 亚洲视频久久久久 | 射射射综合网 | 又黄又爽又无遮挡的视频 | 西西www4444大胆视频 | 亚洲三级在线免费观看 | 人人澡av| 国产一区二区免费在线观看 | 成人免费观看a | 成人久久影院 | 中文字幕 欧美性 | 欧美日韩综合在线观看 | 日韩午夜三级 | 中文字幕在线视频免费播放 | 在线91观看| 日本九九视频 | 九九电影在线 | 久久手机免费观看 | 亚洲精品国偷拍自产在线观看蜜桃 | 天天插天天狠 | 夜夜操天天干, | 国产一区二区播放 | 亚洲在线视频免费 | 手机成人av在线 | 久久精品看片 | 亚洲国产电影在线观看 | 国产片免费在线观看视频 | 亚洲综合视频在线观看 | 成人网大片 | 69绿帽绿奴3pvideos | 91女人18片女毛片60分钟 | 在线国产中文字幕 | 国产不卡在线 | 黄色精品久久 | 99精品久久99久久久久 | 日本一区二区高清不卡 | 精品欧美日韩 | 狠狠色狠狠色综合日日小说 | 中文字幕网站视频在线 | 91精品国产91p65 | 中文在线a∨在线 | av在线电影免费观看 | 久草在线视频中文 | 国产免费成人av | 亚洲国产精品推荐 | 91精品久久久久久综合五月天 | 国产一区欧美日韩 | 99精品一区二区 | 久久精品看片 | 日韩三级视频在线观看 | 一区二区精品在线 | 97偷拍视频 | 狠狠的操| 中文字幕在线观看免费高清电影 | 中文字幕中文字幕在线中文字幕三区 | 国产精品精品国产 | 91高清一区 | 69av久久 | 亚洲精品字幕在线观看 | 国产精品毛片一区二区 | 亚洲小视频在线观看 | 国产精品一区二区 91 | 97超碰中文字幕 | 欧美午夜理伦三级在线观看 | 天天干天天天天 | 中文字幕在线观看国产 | 岛国大片免费视频 | av免费网页 | 波多野结衣在线视频免费观看 | 99在线视频精品 | 夜夜婷婷 | 黄色毛片在线看 | 天天射天天添 | av免费电影网站 | 免费在线成人av | 久久久私人影院 | www久久久久 | 国产手机在线 | 国产精品麻 | 亚a在线 | 亚洲欧美国内爽妇网 | 夜夜视频| 2022中文字幕在线观看 | 久久97久久97精品免视看 | 亚洲资源在线观看 | 精品久久久久久亚洲综合网站 | 麻豆视频在线观看 | 日韩精品中文字幕有码 | 在线观看av国产 | 亚洲综合网站在线观看 | 又色又爽又黄高潮的免费视频 | 夜夜操天天操 | 久久精品久久精品久久39 | 亚洲欧美国产精品18p | 婷婷视频在线 | 欧美久久九九 | 精品毛片在线 | 久久久久久看片 | 中文字幕在线观看免费高清电影 | 国产成人精品久久久 | 在线观看激情av | 激情综合六月 | 中文字幕一区在线观看视频 | 制服丝袜成人在线 | 中国成人一区 | 久久久99精品免费观看 | 中文字幕在线观看亚洲 | 手机成人在线电影 | 涩涩网站在线播放 | 国产精品一区二区久久国产 | 免费又黄又爽视频 | 国产午夜视频在线观看 | 在线观看日韩精品视频 | 91大神免费视频 | 免费在线观看黄色网 | 四虎伊人| 国产精品黑丝在线观看 | 国产精品视频免费观看 | 久久99国产精品 | 超碰97免费在线 | 丝袜网站在线观看 | 免费激情网 | 国内精品久久久久久久影视麻豆 | 五月婷婷激情综合网 | 色网av| 日本精品在线 | 久久激情视频 久久 | 亚洲精品人人 | 欧美激情视频一二区 | 欧美在线视频一区二区三区 | 天天操天天操天天操天天操 | 91成人免费在线 | 国产精品密入口果冻 | 色综合天天综合网国产成人网 | 欧美嫩草影院 | 国产剧情av在线播放 | 黄网站色欧美视频 | 亚洲视频在线观看 | 国产麻豆果冻传媒在线观看 | 91国内产香蕉 | 国产午夜精品一区二区三区 | 久久九九国产视频 | 人人天天夜夜 | 国产高清视频在线免费观看 | 日韩av一区二区三区在线观看 | 久久看片 | 国产精品美女www爽爽爽视频 | 天天射日 | 五月激情婷婷丁香 | 天天做天天爱天天爽综合网 | 麻豆传媒视频在线免费观看 | av高清不卡| 激情综合国产 | 免费观看一区二区三区视频 | 中文免费观看 | 国产91学生粉嫩喷水 | 人人爱人人舔 | 2019中文字幕第一页 | 免费久久久 | 欧美日韩成人一区 | 国产成人久 | 综合久久久久 | 波多野结衣小视频 | 午夜视频日本 | 97偷拍在线视频 | 白丝av免费观看 | 国产在线更新 | 国内精品免费 | 国产午夜精品免费一区二区三区视频 | 久草在线免费色站 | 久久久久久久国产精品视频 | 天天操天天谢 | 99视频精品| 91漂亮少妇露脸在线播放 | 91网页版免费观看 | 成人午夜网| 亚洲欧美日韩国产精品一区午夜 | 免费视频xnxx com | 高清不卡毛片 | 久久婷婷五月综合色丁香 | 亚洲国产中文在线观看 | 99久久超碰中文字幕伊人 | 久久高清国产 | 丁香色婷婷 | av免费观看在线 | 美女久久久久久久 | 国产一区国产二区在线观看 | 最近中文字幕mv | 在线导航av | 成人在线网站观看 | 91精品国产高清自在线观看 | 国产视频 亚洲视频 | 日韩啪啪小视频 | 久久久久国产a免费观看rela | 天天玩天天干天天操 | 91亚色视频| 丝袜美女在线观看 | 蜜臀av性久久久久蜜臀aⅴ四虎 | 久久久久国产成人精品亚洲午夜 | 九九热免费在线视频 | 亚洲不卡在线 | 久久久视频在线 | 久久视频这里有精品 | 亚洲精品自拍视频在线观看 | 狠狠的干 | 国产精品 日韩 | 精品极品在线 | a午夜电影| 中文在线免费看视频 | 玖玖精品在线 | 午夜影院三级 | 亚洲 欧美日韩 国产 中文 | 精品亚洲免a | 久久免费公开视频 | 91视频高清完整版 | 午夜视频在线观看一区二区 | 久久这里 | 国产色综合天天综合网 | 九九久久久久久久久激情 | 久久久久久久久久久国产精品 | 久久综合色综合88 | 亚洲三级视频 | 超碰97久久| 亚洲一区二区三区四区在线视频 | 久久精品精品电影网 | 免费在线一区二区三区 | 亚洲精品视频播放 | 久久久久久久久免费视频 | 狠狠狠色丁香婷婷综合激情 | 中文字幕一区二区三区久久 | 国产精品都在这里 | 色婷婷狠狠18 | 黄色小说视频网站 | 国外av在线| 91精品久久香蕉国产线看观看 | 极品嫩模被强到高潮呻吟91 | 91中文字幕在线播放 | 人人玩人人添人人 | 又黄又刺激 | 亚洲视频网站在线观看 | 日韩精品一区二区三区视频播放 | 热久久99这里有精品 | 久久精品免费播放 | 人人草人| 日韩理论在线视频 | 天天操天天摸天天射 | 欧美超碰在线 | 中文字幕av电影下载 | 久久黄网站 | 免费人成在线观看网站 | 中文字幕美女免费在线 | 久久精品一区二区 | 91爱看片| 在线观看色视频 | 又黄又刺激又爽的视频 | 69热国产视频 | 免费能看的av | 久久国产精品免费一区二区三区 | 欧美黑人xxxx猛性大交 | 成人国产网址 | 91精品999 | 97精品久久 | 91成人免费视频 | 国产一级免费片 | 四虎影视成人精品 | 国产精品色视频 | 国产精品亚洲片夜色在线 | 免费能看的av | 视频二区在线视频 | 成人三级黄色 | 久久午夜免费视频 | 国产中文在线播放 | 久久激情五月婷婷 | 三级小视频在线观看 | 国产成人精品999在线观看 | 国产视频精品在线 | 成人啪啪18免费游戏链接 | 99久热在线精品视频观看 | 婷婷午夜激情 | 国产精品黄色影片导航在线观看 | 四虎成人精品 | 在线激情网 | 久久久精品成人 | 中文字幕日本在线 | 国产在线观看91 | 狠狠色香婷婷久久亚洲精品 | 在线视频观看91 | 91视频这里只有精品 | 狠狠干中文字幕 | 国产麻豆视频在线观看 | 精品网站999www| 国产一级片在线播放 | 免费在线观看黄网站 | 97电影在线看视频 | 日韩3区| 欧美精品一区二区性色 | 亚洲激精日韩激精欧美精品 | 国产一区成人 | 精品不卡av | 久一久久| 国产高清在线a视频大全 | 韩日精品中文字幕 | 欧美日本一区 | 欧美日韩3p | 久久久91精品国产一区二区精品 | 精品人妖videos欧美人妖 | av一区二区在线观看中文字幕 | 日韩欧美一区二区三区在线观看 | 午夜影视av | 麻豆国产精品va在线观看不卡 | 国产人成免费视频 | 久久精国产 | 蜜臀av免费一区二区三区 | 热久在线 | 国产又黄又硬又爽 | 麻豆视频免费版 | 国产欧美综合在线观看 | 色噜噜在线观看 | 国产99久久| 中文字幕色播 | 久久久2o19精品 | 日韩免费大片 | 国产白浆在线观看 | 免费的成人av| 欧美在线视频不卡 | 99国产视频| 国内免费的中文字幕 | 精品免费久久久久久 | 97人人超碰在线 | 天天干天天操av | 高清在线观看av | 成人小视频在线免费观看 | 久久黄色片 | 欧美日本日韩aⅴ在线视频 插插插色综合 | 国产剧情久久 | 国产成人99av超碰超爽 | 国产v在线播放 | 亚洲成人黄色av | 亚洲精品视频中文字幕 | 香蕉视频网址 | 亚洲精品mv在线观看 | 婷婷免费视频 | 96精品高清视频在线观看软件特色 | 91精品久久久久久久91蜜桃 | 午夜精品久久一牛影视 | av一级片在线观看 | 超碰人人草人人 | 欧美日韩精品二区第二页 | 色网免费观看 | 亚洲好视频 | 国产麻豆精品久久一二三 | 91人人爽人人爽人人精88v | 99av国产精品欲麻豆 | 91.麻豆视频 | 久操97 | 精品一区二区电影 | 亚洲综合网站在线观看 | 91免费网站在线观看 | 欧美天堂久久 | 国内精品免费久久影院 | 国产精品18久久久久久久网站 | 精品主播网红福利资源观看 | 日韩精品免费一区 | 国产精品久久久久久久久久新婚 | 日韩资源在线 | 亚洲国产精品va在线看 | 欧美色综合 | 亚洲欧美视频在线观看 | 黄色毛片网站在线观看 | 片网站| 日本高清dvd | 午夜视频福利 | 亚洲日本成人网 | 国产1区在线 | 亚洲丝袜一区 | 国产中年夫妇高潮精品视频 | 一区 二区电影免费在线观看 | 国产精品一区二区av麻豆 | 日韩av一区二区在线影视 | 天天做日日做天天爽视频免费 | 午夜精品久久久久久久99 | 高清av免费看| 久久综合色一综合色88 | 日韩欧美在线免费观看 | 国产二区av | 尤物97国产精品久久精品国产 | 中国一级特黄毛片大片久久 | 国产黄色看片 | 久久久久高清 | 在线成人免费电影 | 99热精品国产一区二区在线观看 | 日韩精品欧美视频 | 久久综合九色欧美综合狠狠 | 97久久精品午夜一区二区 | 三级视频片 | 色综合www| 国产一二三区在线观看 | 日本精品午夜 | 999电影免费在线观看2020 | 成人久久18免费网站图片 | 久久天天躁 | 亚洲精品久久久久久久蜜桃 | 免费电影一区二区三区 | 久久久精品午夜 | 日韩| 四虎影视www | 国产精品九九久久久久久久 | 国产小视频在线观看 | 精品视频在线视频 | 超碰电影在线观看 | 9久久精品 | 国产97在线视频 | 国产一区二区在线观看免费 | 精品一区二区精品 | 久久亚洲热 | 欧美片网站yy| 欧美日韩国产亚洲乱码字幕 | 91色偷偷 | 91av国产视频 | 美女视频一区 | 免费在线观看av网站 | 久久免费的精品国产v∧ | 免费a v视频 | 青青草国产精品视频 | 亚洲午夜剧场 | 久久久久久久久久久久影院 | 日本精品视频在线观看 | 日韩中文字幕a | 久久女教师 | 在线婷婷| 国内精品久久久久久中文字幕 | 97超碰成人在线 | av电影在线免费观看 | 91精品天码美女少妇 | 亚洲视频在线播放 | 国产亚洲精品久 | 欧美一区中文字幕 | 午夜美女av| av电影在线播放 | 婷婷国产一区二区三区 | 色婷久久| 中文字幕乱码在线播放 | 国产98色在线 | 日韩 | 午夜av在线免费 | 国产色综合天天综合网 | 九九热在线精品视频 | 国产一级高清 | 色橹橹欧美在线观看视频高清 | 成年人网站免费观看 | 免费看在线看www777 | 樱空桃av | 91精品久久久久久久久久入口 | 国产一级片免费视频 | 97色婷婷成人综合在线观看 | 国产69精品久久99的直播节目 | 国产成人精品久 | 欧美激情综合色 | 成人在线免费观看网站 | 黄色网大全 | 久久手机免费观看 | 欧美一区二区三区在线看 | 免费a网址 | 日韩午夜精品福利 | 九九热免费精品视频 | 91麻豆精品国产91久久久无限制版 | 国产精品视频内 | 一级免费观看 | 国产精品不卡一区 | 日本免费一二三区 | 天天操天天干天天干 | 午夜精品一区二区三区在线视频 | 999久久精品| 成年人视频在线 | 福利视频| 免费高清在线观看电视网站 | av网站有哪些 | 91影视成人 | 国产视频在线看 | 99福利片 | 91少妇精拍在线播放 | 久久久久久久久久久久久国产精品 | 91中文字幕在线观看 | 国产 日韩 中文字幕 | 999成人免费视频 | 99精品黄色片免费大全 | 丁香六月国产 | 最新99热| 97视频亚洲| 亚洲综合在线播放 | 亚洲日本色 | 午夜成人免费电影 | 国产精品一区专区欧美日韩 | 麻豆视频观看 | 9999精品免费视频 | 久草在线91| 91av视屏| 狠狠色网| 波多野结衣在线播放一区 | 久草在线综合网 | 狠狠躁日日躁狂躁夜夜躁av | 日本公乱妇视频 | 国产高清不卡一区二区三区 | 天天色天天操天天爽 | 久久久网站 | 成人黄色大片 | 亚洲无吗视频在线 | av不卡免费看 | 91av在线视频免费观看 | 日日爽天天爽 | 精品一区二区电影 | 在线精品观看国产 | 日日操操操 | 日韩av手机在线看 | 色插综合 | 444av| 国产精品九九九 | 成人黄色片免费 | 激情 一区二区 | 亚洲视频综合在线 | 小草av在线播放 | 欧美激情视频一区 | 热久久免费视频 | 国产精品成人自产拍在线观看 | av在线精品| 成年人视频在线观看免费 | 亚洲乱亚洲乱妇 | 久久国产精品影片 | 99久久精品无免国产免费 | 欧美另类高清 | 五月开心网 | 激情大尺度视频 | 亚洲国产成人精品电影在线观看 | 久久精品精品电影网 | 亚洲精品视频在线观看网站 | 丁香网五月天 | 日日操夜夜操狠狠操 | 国产第一页精品 | 色婷婷99 | 日韩网站中文字幕 | 久久久久久久久久久久久久免费看 | 午夜精品一区二区三区可下载 | 久草av在线播放 | 中文字幕日韩在线播放 | 欧美一级片播放 | 少妇搡bbbb搡bbb搡aa | 二区三区在线观看 | 97成人在线视频 | 日韩一区二区免费在线观看 | 久久久国产精品人人片99精片欧美一 | 奇米网8888 | 狠狠色丁香久久婷婷综 | 免费看片在线观看 | 91精品爽啪蜜夜国产在线播放 | 欧美男男激情videos | 午夜体验区 | 国产成人av网址 | 欧美精品亚洲精品日韩精品 | 天天干天天干天天色 | 国产精品久久久一区二区 | 日韩欧美精品免费 | 国产成人免费av电影 | 国产黄色片免费 | 久久综合久久久 | 国产精品久久电影观看 | 91精品久久久久久久91蜜桃 | 色综合色综合久久综合频道88 | 国产色视频123区 | 免费在线观看亚洲视频 | 超碰人人在线观看 | 在线观看黄网站 | 日日干夜夜爱 | 国产不卡精品 | 国产一区二区在线观看免费 | 久久久久黄 | 午夜视频在线观看一区 | 日韩欧美一区二区三区在线观看 | 国产精品 亚洲精品 | 国内精品久久久久久久久久清纯 | 日韩欧美综合在线视频 | 亚洲国产精品资源 | 天堂久久电影网 | 日韩理论电影网 | 免费观看成人网 | 国产一区二区三区免费视频 | 91久久国产自产拍夜夜嗨 | 免费毛片一区二区三区久久久 | 夜夜操网| 久久高清片 | 亚洲九九爱 | 久久99精品久久久久久清纯直播 | 久久综合色影院 | 中文字幕在线看视频 | 四虎国产| 中文区中文字幕免费看 | 中文字幕永久 | 中文字幕xxxx | 欧美极度另类性三渗透 | 欧美一区二区免费在线观看 | 亚洲传媒在线 | 婷婷开心久久网 | 97精品超碰一区二区三区 | 亚洲第一区在线播放 | 久久国产精品视频观看 | 五月天天色 | 国产乱视频 | 夜夜视频欧洲 | a视频在线播放 | 久久中文字幕视频 | 99综合视频 | 精品国产乱码一区二区三区在线 | 色视频网址 | 人人爽人人爽人人片 | 日韩av成人在线观看 | 美女免费视频一区二区 | 狠狠色狠狠色终合网 | 人人澡人人澡人人 | 国产福利在线免费 | 午夜久久美女 | 日韩精品免费在线视频 | 亚洲国产精品久久久久婷婷884 | 成人免费观看视频大全 | 成人在线你懂得 | 欧美日韩国产精品爽爽 | 婷婷在线免费视频 | 特级西西444www大精品视频免费看 | 婷婷亚洲综合五月天小说 | 精品91 | 天天射网站 | 一区二区 不卡 | 日本女人的性生活视频 | 91精品久久久久久综合乱菊 | 日本久久高清视频 | 免费黄色激情视频 | 一区二区视频欧美 | 美女网站黄免费 | 久久人人看 | 探花视频在线观看免费 | 亚洲天天摸日日摸天天欢 | 六月婷婷久香在线视频 | 婷婷色中文 | 欧美色操| 国产一级视频在线 | 天天做天天看 | 久草视频资源 | 国产精品 亚洲精品 | 中文亚洲欧美日韩 | 91福利社在线观看 | 超碰人人乐| 极品嫩模被强到高潮呻吟91 | 999视频网站 | 亚洲精品tv久久久久久久久久 | 欧美性极品xxxx娇小 | 视频成人| 久久久久在线视频 | 国产精品久久久久久久久免费 | 九九亚洲精品 | 国产亚洲精品久久久久久久久久 | 999视频网 | 8x成人免费视频 | 欧美aa一级片 | 久久国产网 | 国产一级片在线播放 | 日日日操操 | 久久精品一区二区三区国产主播 | 999日韩| 国产精品成人国产乱 | 色丁香久久 | 欧美黄色成人 | 在线视频欧美精品 | 国产淫片免费看 | 九九热视频在线播放 | 日本成人a | 欧美日韩国产色综合一二三四 | h视频在线看 | 日韩精品欧美专区 | 狠狠操操网 | 日日躁夜夜躁xxxxaaaa | 香蕉视频导航 | 亚洲九九精品 | 久草在线久 | 天天艹天天操 | 亚洲精品国精品久久99热一 | 欧美日韩一区二区在线观看 | 天天玩天天操天天射 | 婷婷六月丁香激情 | 日韩在线不卡视频 | 天天干天天色2020 | 天天操天天干天天综合网 | 国产精品欧美精品 | 国产精品1区2区3区在线观看 | 亚洲国产成人久久 | 国产一级在线观看视频 | 日韩一区二区三区观看 | 福利电影一区二区 | 久久99国产综合精品 | av片免费播放 | 日韩簧片在线观看 | 国产午夜精品在线 | 免费瑟瑟网站 | 7777精品伊人久久久大香线蕉 | 国产午夜一区二区 | 欧美性色黄大片在线观看 | 久久精品韩国 | 久久久久久久久久久影视 | 久久1电影院 | 久久av在线 | 伊人资源视频在线 | 一区二区三区在线视频111 | 色婷婷狠狠五月综合天色拍 | 午夜av电影 | 在线日韩三级 | 日韩视频一二三区 | 国产韩国日本高清视频 | 手机成人在线 | 亚洲精选在线 | 国产精品视频不卡 | 在线亚洲精品 | 中文字幕亚洲字幕 | 91福利国产在线观看 | 欧美一级免费片 | 狠狠干夜夜操天天爽 | 亚洲3级 | 一级片在线 | 午夜久久久久久久久 | 久久综合久久鬼 | 久久精品久久精品久久39 | 激情婷婷网| 亚洲免费资源 | 久久精品在线免费观看 |