澳门新萄京官方网站-www.8455.com-澳门新萄京赌场网址

net中的线程同步基础,Framework达成格局

2019-09-11 作者:www.8455.com   |   浏览(61)

前言:

不久前恰巧写二个主次,须要操作剪切板

效果很简短,只须求从剪切板内读取字符串,然后清空剪切板,然后再把字符串导入剪切板

本人想当然的行使作者最长于的C#来造成那项职业,原因无他,因为.Net框架封装了能落实这种效益的章程

下一场就有了之类代码

澳门新萄京官方网站 1澳门新萄京官方网站 2

 1             string Temp = "";
 2             while (true)
 3             {
 4                 string Tex = Clipboard.GetText().ToString();
 5                 if (!string.IsNullOrWhiteSpace(Tex) && Temp != Tex)
 6                 {
 7                     Clipboard.Clear();
 8                     Clipboard.SetDataObject(Tex, false);
 9                     Temp = Tex;
10                 }
11                 Thread.Sleep(1);
12             }

View Code

这段代码,也是网页上广泛流传的,使用.Net框架操作系统剪切板的法子,当然那些办法在好几意况下很实用

只是在俺那确发生了点难题,首要的主题素材有两点

率先,作者对剪切板的操作必要有实时性,约等于,操作人士复制的须臾间就应当截取到剪切板的数目,管理完后再放入剪切板

结果

 Clipboard.SetDataObject(Tex, false);

没悟出上面那条设置剪切板的一声令下竟然会卡主旨窗口的线程,举例说,笔者在A软件施行了二回复制操作,倘诺应用了上述代码,那么A软件强制线程堵塞大概几百阿秒的样板,反正很影响体验,小编想来是因为该命令会锁定内部存款和储蓄器导致的

那如何是好,本着死马当活马医的神态,笔者特意为该指令启用了四个线程

       Task.Factory.StartNew(()=> 
                    {
                        Clipboard.Clear();
                        Clipboard.SetDataObject(Text, false);
                    });

选用了线程现在,因为操作滞后(线程运维会延迟一会儿,并不实时)了,所以上述难点就像缓慢解决了,但是没悟出出现了新的标题

 string Tex = Clipboard.GetText().ToString();

上述从剪切板得到字符串的一声令下,在默写处境下,会卡滞住,然后程序在一分钟以往,因为超时而被系统注销

emmmmm,在通过几番努力之后,小编终于意识到,即使.Net封装了过多操作系统API的办法,使得有个别IO操作变简单十分多,可是带来的难题也是均等大的,在碰到无法消除的主题素材的时候,会稍为敬谢不敏

于是万般无奈,作者只得吐弃行使过C#成就该项功能,想着幸好功效轻易,并且操作WinAPI其实最佳的恐怕利用C 来写,于是自身用C 复现了上述成效

澳门新萄京官方网站 3澳门新萄京官方网站 4

 1 #include "stdafx.h"
 2 #include <windows.h>
 3 #include <iostream>
 4 using namespace std;
 5 #pragma comment(linker,"/subsystem:windows /entry:mainCRTStartup")
 6 
 7 int main(int argc, _TCHAR* argv[])
 8 {
 9     HANDLE THandle = GlobalAlloc(GMEM_FIXED, 1000);//分配内存
10     char* Temp = (char*)THandle;//锁定内存,返回申请内存的首地址
11     while (true)
12     {
13         HWND hWnd = NULL;
14         OpenClipboard(hWnd);//打开剪切板
15         if (IsClipboardFormatAvailable(CF_TEXT))
16         {
17             HANDLE h = GetClipboardData(CF_TEXT);//获取剪切板数据
18             char* p = (char*)GlobalLock(h);
19             GlobalUnlock(h);
20             if (strcmp(Temp, p))
21             {
22                 EmptyClipboard();//清空剪切板
23                 HANDLE hHandle = GlobalAlloc(GMEM_FIXED, 1000);//分配内存
24                 char* pData = (char*)GlobalLock(hHandle);//锁定内存,返回申请内存的首地址
25                 strcpy(pData, p);
26                 strcpy(Temp, p);
27                 SetClipboardData(CF_TEXT, hHandle);//设置剪切板数据
28                 GlobalUnlock(hHandle);//解除锁定
29             }
30         }
31         CloseClipboard();//关闭剪切板
32         Sleep(500);
33     }
34     return 0;
35 }

View Code

不愧是C ,使用上述代码后,完美兑现笔者须要的功力,况且无论是主程序,依旧自己写的那些顺序,都不会师世卡滞大概不工作的状态了,真是可喜可贺。

那就是说本课程就到此停止。

 

 

 

 

线程安全

style="font-size: 15px;">此类型的具备国有静态(VisualBasic中为Shared)成员对十六线程操作来说都以平安的。但不保证别的实例成员是线程安全的。

在MSDN上时时会看出如此一句话。表示只要程序中有n个线程调用这么些点子,那么那n个线程都以安全的, 然则实例成员就不能确定保证了。

比方说Math.马克斯方法,不管有微微个线程调用,都不会现出线程不安全的情形。

历数三个由于四线程引起的多寡不安全。

澳门新萄京官方网站 5澳门新萄京官方网站 6

static void Main(string[] args){    Stopwatch watch = new Stopwatch();    watch.Start();    int sum = 0;    var t1 = Task.Run =>    {        for (int i = 0; i < 3000000; i  )        {            sum  = i;        }    });    var t2 = Task.Run =>    {        for (int i = 3000000; i < 5000000; i  )        {            sum  = i;        }    });    Task.WaitAll;    Console.WriteLine("和:"  sum);    Console.WriteLine("用时:" watch.ElapsedMilliseconds);}

View Code

和:-1271379077

用时:48

请按放肆键继续...

莫不也可以有人不驾驭,为何产生那些负的了,小编以线程1,线程2独家表示t1,t2运维时候的线程。

线程1 线程2 sum
刚进入循环i=0 0
刚进入循环i=0 0
sum=sum i=0 0=0; i=i 1=1; 0
sum=sum i=0 0=0; i=i 1=1; 0
sum=sum i=0 1=1; i=i 1=2; 1
sum=sum i=1 2=3; i=i 1=3 3
sum=sum i=3 1=4; i=i 1=2; 1

通过表格最终一行,我们开掘,今年线程1,希望的sum=0,可是被线程2修改了。

线程不安全的本来面目:一个线程想要的数量被其余线程退换了

怎么消除?作者想许几个人大概都会lock,lock,lock

澳门新萄京官方网站 7澳门新萄京官方网站 8

private static readonly object objAsync = new object();static void Main(string[] args){    Stopwatch watch = new Stopwatch();    watch.Start();    int sum = 0;    var t1 = Task.Run =>    {        for (int i = 0; i < 3000000; i  )        {            lock             {                sum  = i;            }        }    });    var t2 = Task.Run =>    {        for (int i = 3000000; i < 5000000; i  )        {            lock             {                sum  = i;            }        }    });    Task.WaitAll;    Console.WriteLine("和:"   sum);    Console.WriteLine("用时:"   watch.ElapsedMilliseconds);}

View Code

澳门新萄京官方网站,和:1642668640

用时:461

请按猖狂键继续...

思考

骨子里这里的例子完全能够不经过加锁来缓慢解决,但是为了展现加锁带来的负面影响,就很醒目了,线程同步是急需代价的;

线程安全

style="font-size: 15px">此类型的有所公共静态(Visual Basic 中为 Shared)成员对十六线程操作来说都以平安的。但不保证别的实例成员是线程安全的。

在MSDN上平常会见到那般一句话。表示一旦程序中有n个线程调用那些法子,那么这n个线程都以安枕无忧的, 可是实例成员就不能够保证了。

比如说Math.马克斯方法,不管有个别许个线程调用,都不会油然则生线程不安全的图景。

列举一个是因为二十多线程引起的数额不安全。

澳门新萄京官方网站 9澳门新萄京官方网站 10

static void Main(string[] args)
{
    Stopwatch watch = new Stopwatch();
    watch.Start();
    int sum = 0;
    var t1 = Task.Run(() =>
    {
        for (int i = 0; i < 3000000; i  )
        {
            sum  = i;
        }
    });
    var t2 = Task.Run(() =>
    {
        for (int i = 3000000; i < 5000000; i  )
        {
            sum  = i;
        }
    });

    Task.WaitAll(t1, t2);
    Console.WriteLine("和:"  sum);
    Console.WriteLine("用时:" watch.ElapsedMilliseconds);
}

View Code

和:-1271379077

用时:48

请按自便键继续. . .

可能也是有人不知晓,为啥产生那几个负的了,作者以线程1,线程2独家表示t1,t2运转时候的线程。

 线程1  线程2  sum
 刚进入循环i=0    0
   刚进入循环i=0  0
   sum=sum i=0 0=0;    i=i 1=1;  0
 sum=sum i=0 0=0;    i=i 1=1;    0
   sum=sum i=0 1=1;    i=i 1=2;  1
   sum=sum i=1 2=3;    i=i 1=3  3
 sum=sum i=3 1=4;    i=i 1=2;       1

 通过表格最终一行,大家发掘,那个时候线程1,希望的sum=0,但是被线程2修改了。

 线程不安全的本色:三个线程想要的多寡被其余线程改变了

 怎么消除?笔者想大多个人可能都会(当然,笔者也是)lock,lock,lock

澳门新萄京官方网站 11澳门新萄京官方网站 12

private static readonly object objAsync = new object();
static void Main(string[] args)
{
    Stopwatch watch = new Stopwatch();
    watch.Start();
    int sum = 0;
    var t1 = Task.Run(() =>
    {
        for (int i = 0; i < 3000000; i  )
        {
            lock (objAsync)
            {
                sum  = i;
            }
        }
    });
    var t2 = Task.Run(() =>
    {
        for (int i = 3000000; i < 5000000; i  )
        {
            lock (objAsync)
            {
                sum  = i;
            }
        }
    });

    Task.WaitAll(t1, t2);
    Console.WriteLine("和:"   sum);
    Console.WriteLine("用时:"   watch.ElapsedMilliseconds);
}

View Code

和:1642668640

用时:461

请按大肆键继续. . .

思考

实质上这里的例证完全能够不通过加锁来化解,然而为了表现加锁带来的负面影响,就很分明了,线程同步是急需代价的;

以下是本文

想着,既然自个儿能用C 调用WinAPI完美兑现作者要求的机能,并且C#也能调用非托管的代码来实践WinAPI,那么小编不是足以把上边C 写的代码移植到C#里面实行?说干就干

首先,C#调用WinAPI须求先表达

        [DllImport("User32")]
        internal static extern bool OpenClipboard(IntPtr hWndNewOwner);

        [DllImport("User32")]
        internal static extern bool CloseClipboard();

        [DllImport("User32")]
        internal static extern bool EmptyClipboard();

        [DllImport("User32")]
        internal static extern bool IsClipboardFormatAvailable(int format);

        [DllImport("User32")]
        internal static extern IntPtr GetClipboardData(int uFormat);

        [DllImport("User32", CharSet = CharSet.Unicode)]
        internal static extern IntPtr SetClipboardData(int uFormat, IntPtr hMem);

操作剪切板供给调用的API大概就地点这个

有了API今后,我们还必要和煦手动封装方法

     internal static void SetText(string text)
        {
            if (!OpenClipboard(IntPtr.Zero))
        {
        SetText(text);
        return;
        }
            EmptyClipboard();
            SetClipboardData(13, Marshal.StringToHGlobalUni(text));
            CloseClipboard();
        }

        internal static string GetText(int format)
        {
            string value = string.Empty;
            OpenClipboard(IntPtr.Zero);
            if (IsClipboardFormatAvailable(format))
            {
                IntPtr ptr = NativeMethods.GetClipboardData(format);
                if (ptr != IntPtr.Zero)
                {
                    value = Marshal.PtrToStringUni(ptr);
                }
            }
            CloseClipboard();
            return value;
        }

小编们也就用到四个法子,从剪切板获得文本和设置文本到剪切板,哦关于SetClipboardData的首先个参数13是怎么来的主题素材,其实这些剪切板的格式参数,下边有一张表,正是自从这里来的

澳门新萄京官方网站 13澳门新萄京官方网站 14

public static class ClipboardFormat
{
    /// <summary>
    /// Text format. Each line ends with a carriage return/linefeed (CR-LF) combination. A null character signals
    /// the end of the data. Use this format for ANSI text.
    /// </summary>
    public const int CF_TEXT = 1;

    /// <summary>
    /// A handle to a bitmap (<c>HBITMAP</c>).
    /// </summary>
    public const int CF_BITMAP = 2;

    /// <summary>
    /// Handle to a metafile picture format as defined by the <c>METAFILEPICT</c> structure. When passing a
    /// <c>CF_METAFILEPICT</c> handle by means of DDE, the application responsible for deleting <c>hMem</c> should
    /// also free the metafile referred to by the <c>CF_METAFILEPICT</c> handle.
    /// </summary>
    public const int CF_METAFILEPICT = 3;

    /// <summary>
    /// Microsoft Symbolic Link (SYLK) format.
    /// </summary>
    public const int CF_SYLK = 4;

    /// <summary>
    /// Software Arts' Data Interchange Format.
    /// </summary>
    public const int CF_DIF = 5;

    /// <summary>
    /// Tagged-image file format.
    /// </summary>
    public const int CF_TIFF = 6;

    /// <summary>
    /// Text format containing characters in the OEM character set. Each line ends with a carriage return/linefeed
    /// (CR-LF) combination. A null character signals the end of the data.
    /// </summary>
    public const int CF_OEMTEXT = 7;

    /// <summary>
    /// A memory object containing a <c>BITMAPINFO</c> structure followed by the bitmap bits.
    /// </summary>
    public const int CF_DIB = 8;

    /// <summary>
    /// Handle to a color palette. Whenever an application places data in the clipboard that depends on or assumes
    /// a color palette, it should place the palette on the clipboard as well. If the clipboard contains data in
    /// the <see cref="CF_PALETTE"/> (logical color palette) format, the application should use the
    /// <c>SelectPalette</c> and <c>RealizePalette</c> functions to realize (compare) any other data in the
    /// clipboard against that logical palette. When displaying clipboard data, the clipboard always uses as its
    /// current palette any object on the clipboard that is in the <c>CF_PALETTE</c> format.
    /// </summary>
    public const int CF_PALETTE = 9;

    /// <summary>
    /// Data for the pen extensions to the Microsoft Windows for Pen Computing.
    /// </summary>
    public const int CF_PENDATA = 10;

    /// <summary>
    /// Represents audio data more complex than can be represented in a CF_WAVE standard wave format.
    /// </summary>
    public const int CF_RIFF = 11;

    /// <summary>
    /// Represents audio data in one of the standard wave formats, such as 11 kHz or 22 kHz PCM.
    /// </summary>
    public const int CF_WAVE = 12;

    /// <summary>
    /// Unicode text format. Each line ends with a carriage return/linefeed (CR-LF) combination. A null character
    /// signals the end of the data.
    /// </summary>
    public const int CF_UNICODETEXT = 13;

    /// <summary>
    /// A handle to an enhanced metafile (<c>HENHMETAFILE</c>).
    /// </summary>
    public const int CF_ENHMETAFILE = 14;

    /// <summary>
    /// A handle to type <c>HDROP</c> that identifies a list of files. An application can retrieve information
    /// about the files by passing the handle to the <c>DragQueryFile</c> function.
    /// </summary>
    public const int CF_HDROP = 15;

    /// <summary>
    /// The data is a handle to the locale identifier associated with text in the clipboard. When you close the
    /// clipboard, if it contains <c>CF_TEXT</c> data but no <c>CF_LOCALE</c> data, the system automatically sets
    /// the <c>CF_LOCALE</c> format to the current input language. You can use the <c>CF_LOCALE</c> format to
    /// associate a different locale with the clipboard text.
    /// An application that pastes text from the clipboard can retrieve this format to determine which character
    /// set was used to generate the text.
    /// Note that the clipboard does not support plain text in multiple character sets. To achieve this, use a
    /// formatted text data type such as RTF instead. 
    /// The system uses the code page associated with <c>CF_LOCALE</c> to implicitly convert from
    /// <see cref="CF_TEXT"/> to <see cref="CF_UNICODETEXT"/>. Therefore, the correct code page table is used for
    /// the conversion.
    /// </summary>
    public const int CF_LOCALE = 16;

    /// <summary>
    /// A memory object containing a <c>BITMAPV5HEADER</c> structure followed by the bitmap color space
    /// information and the bitmap bits.
    /// </summary>
    public const int CF_DIBV5 = 17;

    /// <summary>
    /// Owner-display format. The clipboard owner must display and update the clipboard viewer window, and receive
    /// the <see cref="ClipboardMessages.WM_ASKCBFORMATNAME"/>, <see cref="ClipboardMessages.WM_HSCROLLCLIPBOARD"/>,
    /// <see cref="ClipboardMessages.WM_PAINTCLIPBOARD"/>, <see cref="ClipboardMessages.WM_SIZECLIPBOARD"/>, and
    /// <see cref="ClipboardMessages.WM_VSCROLLCLIPBOARD"/> messages. The <c>hMem</c> parameter must be <c>null</c>.
    /// </summary>
    public const int CF_OWNERDISPLAY = 0x0080;

    /// <summary>
    /// Text display format associated with a private format. The <c>hMem</c> parameter must be a handle to data
    /// that can be displayed in text format in lieu of the privately formatted data.
    /// </summary>
    public const int CF_DSPTEXT = 0x0081;

    /// <summary>
    /// Bitmap display format associated with a private format. The <c>hMem</c> parameter must be a handle to
    /// data that can be displayed in bitmap format in lieu of the privately formatted data.
    /// </summary>
    public const int CF_DSPBITMAP = 0x0082;

    /// <summary>
    /// Metafile-picture display format associated with a private format. The <c>hMem</c> parameter must be a
    /// handle to data that can be displayed in metafile-picture format in lieu of the privately formatted data.
    /// </summary>
    public const int CF_DSPMETAFILEPICT = 0x0083;

    /// <summary>
    /// Enhanced metafile display format associated with a private format. The <c>hMem</c> parameter must be a
    /// handle to data that can be displayed in enhanced metafile format in lieu of the privately formatted data.
    /// </summary>
    public const int CF_DSPENHMETAFILE = 0x008E;

    /// <summary>
    /// Start of a range of integer values for application-defined GDI object clipboard formats. The end of the
    /// range is <see cref="CF_GDIOBJLAST"/>. Handles associated with clipboard formats in this range are not
    /// automatically deleted using the <c>GlobalFree</c> function when the clipboard is emptied. Also, when using
    /// values in this range, the <c>hMem</c> parameter is not a handle to a GDI object, but is a handle allocated
    /// by the <c>GlobalAlloc</c> function with the <c>GMEM_MOVEABLE</c> flag.
    /// </summary>
    public const int CF_GDIOBJFIRST = 0x0300;

    /// <summary>
    /// See <see cref="CF_GDIOBJFIRST"/>.
    /// </summary>
    public const int CF_GDIOBJLAST = 0x03FF;

    /// <summary>
    /// Start of a range of integer values for private clipboard formats. The range ends with
    /// <see cref="CF_PRIVATELAST"/>. Handles associated with private clipboard formats are not freed
    /// automatically; the clipboard owner must free such handles, typically in response to the
    /// <see cref="ClipboardMessages.WM_DESTROYCLIPBOARD"/> message.
    /// </summary>
    public const int CF_PRIVATEFIRST = 0x0200;

    /// <summary>
    /// See <see cref="CF_PRIVATEFIRST"/>.
    /// </summary>
    public const int CF_PRIVATELAST = 0x02FF;
}

View Code

在C 里面是不用钦赐数字的,只供给用CF_UNICODETEXT就行,然则.Net里面应该没有对号入座的索引表,所以不得不手动输入(作者那边是为着阐明用才特意用数字,自个儿代码那是索引的枚举类)

上边七个干活做完事后,就能够兑现效果与利益了,功效代码如下

                   var LastS = string.Empty;
                   while (!CancelInfoClipboard.IsCancellationRequested)
                   {
                       var Temp = ClipboardControl.GetText(ClipboardFormat.CF_UNICODETEXT);
                       if (!string.IsNullOrEmpty(Temp) && Temp != LastS)
                       {
                           ClipboardControl.SetText(Temp);
                           LastS = Temp;
                       }
                       Thread.Sleep(50);
                   }

是否和最开始显得的调用.Net框架的不二秘技一致(笑),可是使用底层API完结的法力,就从未那么多杂乱无章的Bug了,自个儿也很清楚毕竟实现了啥功用,同期也获取了广大新知识(主假若非托管代码调用的时候的注意事项什么的,还会有,向非托管代码传递数据的时候,最广大用马尔斯hal类里面的主意,不然恐怕会出错,终究这些类就是极其为非托管代码而设置的)

 

 

net中的线程同步基础,Framework达成格局。原子操作

何以要给sum = i加锁,因为它是不原子操作,八个线程同有时间操作就能发出竞态条件

style="font-size: 14px;">百度百科:原子操作是没有需求一齐的,那是.Net澳门新萄京官方网站 15二十八线程编制程序的老调重弹了。所谓原子操作是指不会被线程调节机制打断的操作;这种操作一旦最早,就径直运转到截止,中间不会有别的context switch

在.net中,CLLacrosse保证一下类别的读写是原子性的。Boolean、Char、Byte、Int16、Int32、IntPtr、Single、引用类型。不包罗Double、Int64、Int64.

原理:CLENVISION保障4字节以内的根底项指标读写是原子性的。因为对此Int64类型变量的读写,只要施行两回相应的机器指令。

原子操作

干什么要给sum = i加锁,因为它是不原子操作,五个线程同有的时候间操作就能够发出竞态条件

style="font-size: 14px">百度百科:原子操作是不须求一块的,这是.Net澳门新萄京官方网站 16多线程编制程序的沉滓泛起了。所谓原子操作是指不会被线程调节机制打断的操作;这种操作一旦开首,就一向运维到停止,中间不会有另外context switch (换来另三个线程)

在.net中,CL福睿斯保险一下门类的读写是原子性的。Boolean、Char、(S)Byte、(U)Int16、(U)Int32、(U)IntPtr、Single、援用类型。不富含Double、Int64、(U)Int64.

规律:CLHighlander保险4字节之内的根底项指标读写是原子性的。因为对于Int64类型变量的读写,只要实践三次相应的机器指令。

接下去是新的意识

在商讨MSDN上边境海关于剪切板的API的时候,开采了二个函数

bool AddClipboardFormatListener(HWND hwnd);

基于描述来说,是加上二个剪切板的监察和控制,在剪切板有任何改换的时候,文告你所钦定的句柄的窗口,小编一想,这不就是自己所急需的么,有了如此三个API现在,其实本人上边所显示的,使用死循环轮询剪切板的艺术就变得很傻逼,何况也很轻巧失误了,于是,基于这几个新意识的API,笔者再也退换了全部的程序逻辑,反而比原来的贯彻更为简明了。

首先大家要求二个新的窗口仍旧控件来收取Windows新闻更新后所发来的新闻,只要New 多个form就行

        public Form2()
        {
            InitializeComponent();
            AddClipboardFormatListener(this.Handle);
        }

下一场大家在开头化组件的通令后边,把施用增加剪切板监听的API把当下窗口的句柄发给系统,那样系统在抽出到剪切板退换的吩咐后,会把消息发给当前窗口

接下来我们须求复写WndProc方法

   protected override void WndProc(ref Message m)
        {
            if (m.Msg == 0x031D && Onice)
            {var Temp = ClipboardControl.GetText(ClipboardFormat.CF_UNICODETEXT);
                    if (!string.IsNullOrEmpty(Temp))
                    {
                        ClipboardControl.SetText(Temp);
                        Onice = false;
                    }
            }
            else if (!Onice)
            {
                Onice = true;
            }
            else
            {
                base.WndProc(ref m);
            }
        }
   private bool Onice = true;

首先WndProc若是是Form类下边多少个极其用来接过系统一发布送过来的消息的法子

然后关于m.Msg == 0x031D的0x031D在WinAPI定义上的意义是WM_CLIPBOAPAJERODUPDATE ,也便是剪切板更新事件,那个通过寻找MSDN能够找到

上边没有非常意外的函数,便是有有些亟需注意,大家这里设置了剪切板数据,也正是进行了叁回立异,所以会在那不常而重新爆发剪切板更新事件,然后又会打招呼那个点子,然后就能够产生死循环,小编在此地用了八个布尔决断来经过布尔状态调节是不是截取剪切板,不仅独有未有越来越好的方法来完毕

以上

 

 

线程同步

对此线程同步,有客商方式和基础形式两种,由于客商情势速度显著快于内核方式。

客户情势:提供特种的CPU指令来和煦线程,意味着和睦是在硬件中生出的,劣点是操作系统和CL揽胜恒久检查实验不到一个线程在客商情势上过不去了,线程池也不会创设新的线程来替换不常阻塞的线程,这几个CPU指令阻塞不够长的岁月。

基本情势:由操作系统提供,优点通过基础方式得到另外线程具有的财富时,Windows会阻塞线程以制止它浪费CPU时间,财富可用时,恢复生机线程,允许它访谈财富。

线程同步

对于线程同步,有客户情势和基本格局二种,由于顾客情势速度分明快于内核格局。

顾客情势:提供独特的CPU指令来协和线程,意味着和睦是在硬件中发生的,弱点是操作系统和CLCR-V恒久检查实验不到三个线程在用户格局上围堵了,线程池也不会创立新的线程来替换偶尔阻塞的线程,那么些CPU指令阻塞非常的短的日子。

根本情势:由操作系统提供,优点通过基础情势获得别的线程具备的能源时,Windows会阻塞线程以幸免它浪费CPU时间,能源可用时,苏醒线程,允许它访问能源。

 

 

Volatile

澳门新萄京官方网站 17澳门新萄京官方网站 18

private int flag = 0;private int number = 0;public void Thread1(){    number = 5;    flag = 1;}public void Thread2(){    if (flag == 1)    {        Console.WriteLine;    }}

View Code

Thread第11中学先将value赋值为0,再把flag赋值为1,按道理说在Thread第22中学,假使flag为1了,那么value必定为5,实际上不是那样子的,C#编写翻译器,JIT编写翻译器,CPU会优化代码,导致Thread第11中学实行各种不鲜明,作者也是服了,那有啥用,为啥要做如此的优化!!!

能够通过Volatile.Read或Volatile.Write阻止这种优化。Volatile.Read保险提供的变量中的值在调用时读取,并且在此之前的加载和储存操作是依据大家的编码顺序,Volatile.Write有限支撑提供的变量中的值在调用时写入,而且之后的加载和积存操作是依据大家的编码顺序。volatile无法用来线程同步。这里有一点坑啊以为

澳门新萄京官方网站 19澳门新萄京官方网站 20

private int flag = 0;private int number = 0;public void Thread1(){    number = 5;    Volatile.Write(ref flag, 1);}public void Thread2(){    if (Volatile.Read(ref flag) == 1)    {        Console.WriteLine;    }}

View Code

也得以通过volatile关键字来定义变量,效果是一致的。

澳门新萄京官方网站 21澳门新萄京官方网站 22

private volatile int flag = 0;private int number = 0;public void Thread1(){    number = 5;    flag = 1;}public void Thread2(){    if (flag == 1)    {        Console.WriteLine;    }}

View Code

Volatile

澳门新萄京官方网站 23澳门新萄京官方网站 24

private int flag = 0;
private int number = 0;
public void Thread1()
{
    number = 5;
    flag = 1;
}
public void Thread2()
{
    if (flag == 1)
    {
        Console.WriteLine(number);
    }
}

View Code

Thread第11中学先将value赋值为0,再把flag赋值为1,按道理说在Thread第22中学,假使flag为1了,那么value必定为5,实际上不是那样子的,C#编写翻译器,JIT编写翻译器,CPU会优化代码,导致Thread第11中学实践顺序不醒目,作者也是服了,那有哪些用,为何要做如此的优化!!!

能够透过Volatile.Read或Volatile.Write阻止这种优化。Volatile.Read有限支撑提供的变量中的值在调用时读取,何况在此之前的加载和储存操作是遵照大家的编码顺序,Volatile.Write有限支撑提供的变量中的值在调用时写入,何况之后的加载和储存操作是遵照我们的编码顺序。volatile不能够用来线程同步。这里有一点坑啊以为

澳门新萄京官方网站 25澳门新萄京官方网站 26

private int flag = 0;
private int number = 0;
public void Thread1()
{
    number = 5;
    Volatile.Write(ref flag, 1);
}
public void Thread2()
{
    if (Volatile.Read(ref flag) == 1)
    {
        Console.WriteLine(number);
    }
}

View Code

 也得以透过volatile关键字来定义变量,效果是均等的。

澳门新萄京官方网站 27澳门新萄京官方网站 28

private volatile int flag = 0;
private int number = 0;
public void Thread1()
{
    number = 5;
    flag = 1;
}
public void Thread2()
{
    if (flag == 1)
    {
        Console.WriteLine(number);
    }
}

View Code

Asp.Net.Identity为啥物请自行检索,也可转化此小说

Asp.Net.Identity为什么物请自行检索,也可转化此文章

Interlocked

Interlocked提供了一组用来原子操作的静态方法。

int Add(ref int location1, int value) 原子加法操作,支持long
int Decrement(ref int location) 原子自减
int Decrement(ref int location) 原子自增
int Exchange(ref int location1, int value) 原子交换
int CompareExchange(ref int location1, int value, int comparand) 原子比较交换

这个艺术基本都同期援救int、long、double、float、object的。解释一下最后二个措施。

CompareExchange:假若location1等于comparand,就把value赋值给它。语法和底下的代码一样。

int location1 = 1;int comparand = 1;int value = 2;if(location1 == comparand){    location1 = value;}

只要上边的花色无法满足吗?

使用Interlocked实现自旋锁让多个线程在原地打转,幸免它跑去和另贰个线程竞争财富,然而会浪费宝贵的CPU时间,自旋锁应该用于掩护这些会施行的不得了快的代码区域。

澳门新萄京官方网站 29澳门新萄京官方网站 30

struct SimpleSpinLock{    private int m_ResourceInUse;    public void Enter()    {        while (true)        {            if (Interlocked.Exchange(ref m_ResourceInUse, 1) == 0            {                return;            }        }    }    public void Leave()    {        Volatile.Write(ref m_ResourceInUse, 0);    }} static int sum = 0;static void Main(string[] args){    Stopwatch watch = new Stopwatch();    watch.Start();    SimpleSpinLock  g= new SimpleSpinLock();    var t1 = Task.Run =>    {        for (int i = 0; i < 3000000; i  )        {            g.Enter();            sum  = i;            g.Leave();        }    });    var t2 = Task.Run =>    {        for (int i = 3000000; i < 5000000; i  )        {            g.Enter();            sum  = i;            g.Leave();        }    });    Task.WaitAll;    Console.WriteLine("和:"   sum);    Console.WriteLine("用时:"   watch.ElapsedMilliseconds);}

View Code

和:1642668640

用时:610

请按大肆键继续...

Interlocked

 Interlocked提供了一组用来原子操作的静态方法。

 int Add(ref int location1, int value)  原子加法操作,支持long
 int Decrement(ref int location)  原子自减
 int Decrement(ref int location)  原子自增
 int Exchange(ref int location1, int value)  原子交换
 int CompareExchange(ref int location1, int value, int comparand)  原子比较交换

这个主意基本都同期协理int、long、double、float、object的。解释一下最终三个方式。

CompareExchange:借使location1等于comparand,就把value赋值给它。语法和上面包车型地铁代码同样。

int location1 = 1;
int comparand = 1;
int value = 2;
if(location1 == comparand)
{
    location1 = value;
}

假若下边包车型地铁品种无法满意呢?

使用Interlocked实现自旋锁让一个线程在原地打转,幸免它跑去和另三个线程竞争财富,然则会浪费宝贵的CPU时间,自旋锁应该用于维护那多少个会施行的不行快的代码区域。

澳门新萄京官方网站 31澳门新萄京官方网站 32

struct SimpleSpinLock
{
    private int m_ResourceInUse;
    public void Enter()
    {
        while (true)
        {
            if (Interlocked.Exchange(ref m_ResourceInUse, 1) == 0
            {
                return;
            }
        }
    }
    public void Leave()
    {
        Volatile.Write(ref m_ResourceInUse, 0);
    }
}
 static int sum = 0;
static void Main(string[] args)
{
    Stopwatch watch = new Stopwatch();
    watch.Start();
    SimpleSpinLock  g= new SimpleSpinLock();
    var t1 = Task.Run(() =>
    {
        for (int i = 0; i < 3000000; i  )
        {
            g.Enter();
            sum  = i;
            g.Leave();
        }
    });
    var t2 = Task.Run(() =>
    {
        for (int i = 3000000; i < 5000000; i  )
        {
            g.Enter();
            sum  = i;
            g.Leave();
        }
    });
    Task.WaitAll(t1, t2);
    Console.WriteLine("和:"   sum);
    Console.WriteLine("用时:"   watch.ElapsedMilliseconds);
}

View Code

和:1642668640

用时:610

请按大肆键继续. . .

本来微软现已帮大家将授权、认证以及数据仓库储存款和储蓄都依次处理好了。可是总有这种情景,如大家未来的项目是现已存在了数据库,且库里已经有顾客、剧中人物等音信表,不过

本来微软一度帮大家将授权、认证以及数据仓库储存款和储蓄都逐项处理好了。但是总有这种情景,如作者辈今日的门类是曾经存在了数据库,且Curry已经有客商、角色等音讯表,可是

WaitHandle

抽象类,封装贰个Windows内核查象句柄。

澳门新萄京官方网站 33澳门新萄京官方网站 34

  //封装等待对共享资源的独占访问的操作系统特定的对象。  public abstract class WaitHandle : MarshalByRefObject, IDisposable   {        public const int WaitTimeout = 258;        protected static readonly IntPtr InvalidHandle;        protected WaitHandle();        [Obsolete("Use the SafeWaitHandle property instead.")]        public virtual IntPtr Handle { get; set; }    //获取或设置本机操作系统句柄。        public SafeWaitHandle SafeWaitHandle { get; set; }    //向一个 System.Threading.WaitHandle 发出信号并等待另一个,指定超时间隔为 32 位有符号整数,并指定在进入等待前是否退出上下文的同步域。        public static bool SignalAndWait(WaitHandle toSignal, WaitHandle toWaitOn);    //等待指定数组中的所有元素收到信号,使用 System.Int32 值指定时间间隔,并指定是否在等待之前退出同步域。都收到信号返回true        public static bool WaitAll(WaitHandle[] waitHandles, int millisecondsTimeout);    //等待指定数组中的任一元素收到信号,使用 32 位带符号整数指定时间间隔并指定是否在等待之前退出同步域。返回收到信号的对象的数组索引        public static int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext);        public virtual void Close();    //阻止当前线程,直到当前的 System.Threading.WaitHandle 收到信号为止,同时使用 32 位带符号整数指定时间间隔,并指定是否在等待之前退出同步域。收到信号返回true        public virtual bool WaitOne(TimeSpan timeout, bool exitContext);        protected virtual void Dispose(bool explicitDisposing);    }

View Code

WaitHandle

 抽象类,封装三个Windows内核查象句柄。

澳门新萄京官方网站 35澳门新萄京官方网站 36

  //封装等待对共享资源的独占访问的操作系统特定的对象。
  public abstract class WaitHandle : MarshalByRefObject, IDisposable
   {
        public const int WaitTimeout = 258;

        protected static readonly IntPtr InvalidHandle;

        protected WaitHandle();

        [Obsolete("Use the SafeWaitHandle property instead.")]
        public virtual IntPtr Handle { get; set; }
    //获取或设置本机操作系统句柄。

        public SafeWaitHandle SafeWaitHandle { get; set; }

    //向一个 System.Threading.WaitHandle 发出信号并等待另一个,指定超时间隔为 32 位有符号整数,并指定在进入等待前是否退出上下文的同步域。
        public static bool SignalAndWait(WaitHandle toSignal, WaitHandle toWaitOn);

    //等待指定数组中的所有元素收到信号,使用 System.Int32 值指定时间间隔,并指定是否在等待之前退出同步域。都收到信号返回true
        public static bool WaitAll(WaitHandle[] waitHandles, int millisecondsTimeout);

    //等待指定数组中的任一元素收到信号,使用 32 位带符号整数指定时间间隔并指定是否在等待之前退出同步域。返回收到信号的对象的数组索引
        public static int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext);

        public virtual void Close();

    //阻止当前线程,直到当前的 System.Threading.WaitHandle 收到信号为止,同时使用 32 位带符号整数指定时间间隔,并指定是否在等待之前退出同步域。收到信号返回true
        public virtual bool WaitOne(TimeSpan timeout, bool exitContext);

        protected virtual void Dispose(bool explicitDisposing);
    }

View Code

笔者们照旧贪婪想采用微软的授权、认证类库。这里自个儿就来其实奉行下到底可行不可行~

咱俩依然贪婪想使用微软的授权、认证类库。这里笔者就来实在实行下到底可行不可行~

EventWaitHandle

澳门新萄京官方网站 37澳门新萄京官方网站 38

//表示一个线程同步事件。public class EventWaitHandle : WaitHandle{    //     初始化 System.Threading.EventWaitHandle 类的新实例,并指定在此调用后创建的等待句柄最初是否处于终止状态,它是自动重置还是手动重置,系统同步事件的名称,一个    //     Boolean 变量(其值在调用后表示是否创建了已命名的系统事件),以及应用于已命名的事件的访问控制安全性。    public EventWaitHandle(bool initialState, EventResetMode mode, string name, out bool createdNew, EventWaitHandleSecurity eventSecurity);    //     打开指定名称为同步事件。    public static EventWaitHandle OpenExisting(string name);    //     用安全访问权限打开指定名称为同步事件。    public static EventWaitHandle OpenExisting(string name, EventWaitHandleRights rights);    //     打开指定名称为同步事件,并返回指示操作是否成功的值。    public static bool TryOpenExisting(string name, out EventWaitHandle result);    //     用安全访问权限打开指定名称为同步事件,并返回指示操作是否成功的值。    public static bool TryOpenExisting(string name, EventWaitHandleRights rights, out EventWaitHandle result);    //     获取 System.Security.AccessControl.EventWaitHandleSecurity 对象,该对象表示由当前 System.Threading.EventWaitHandle    //     对象表示的已命名系统事件的访问控制安全性。    public EventWaitHandleSecurity GetAccessControl();    //     将事件状态设置为非终止状态,导致线程阻止。    public bool Reset();    //     将事件状态设置为终止状态,允许一个或多个等待线程继续。    public bool Set();    //     设置已命名的系统事件的访问控制安全性。    public void SetAccessControl(EventWaitHandleSecurity eventSecurity);}

View Code

EventWaitHandle

澳门新萄京官方网站 39澳门新萄京官方网站 40

//表示一个线程同步事件。
public class EventWaitHandle : WaitHandle
{
    //     初始化 System.Threading.EventWaitHandle 类的新实例,并指定在此调用后创建的等待句柄最初是否处于终止状态,它是自动重置还是手动重置,系统同步事件的名称,一个
    //     Boolean 变量(其值在调用后表示是否创建了已命名的系统事件),以及应用于已命名的事件(如果创建了该事件)的访问控制安全性。
    public EventWaitHandle(bool initialState, EventResetMode mode, string name, out bool createdNew, EventWaitHandleSecurity eventSecurity);

    //     打开指定名称为同步事件(如果已经存在)。
    public static EventWaitHandle OpenExisting(string name);

    //     用安全访问权限打开指定名称为同步事件(如果已经存在)。
    public static EventWaitHandle OpenExisting(string name, EventWaitHandleRights rights);

    //     打开指定名称为同步事件(如果已经存在),并返回指示操作是否成功的值。
    public static bool TryOpenExisting(string name, out EventWaitHandle result);

    //     用安全访问权限打开指定名称为同步事件(如果已经存在),并返回指示操作是否成功的值。
    public static bool TryOpenExisting(string name, EventWaitHandleRights rights, out EventWaitHandle result);

    //     获取 System.Security.AccessControl.EventWaitHandleSecurity 对象,该对象表示由当前 System.Threading.EventWaitHandle
    //     对象表示的已命名系统事件的访问控制安全性。
    public EventWaitHandleSecurity GetAccessControl();

    //     将事件状态设置为非终止状态,导致线程阻止。
    public bool Reset();

    //     将事件状态设置为终止状态,允许一个或多个等待线程继续。
    public bool Set();

    //     设置已命名的系统事件的访问控制安全性。
    public void SetAccessControl(EventWaitHandleSecurity eventSecurity);
}

View Code

首先步、新建八个Asp.Net MVC框架的web工程

先是步、新建一个Asp.Net MVC框架的web工程

AutoResetEvent

澳门新萄京官方网站 41澳门新萄京官方网站 42

//// 摘要://     通知正在等待的线程已发生事件。此类不能被继承。public sealed class AutoResetEvent : EventWaitHandle{    //    // 摘要:    //     使用 Boolean 值(指示是否将初始状态设置为终止的)初始化 System.Threading.AutoResetEvent 类的新实例。    //    // 参数:    //   initialState:    //     若要将初始状态设置为终止,则为 true;若要将初始状态设置为非终止,则为 false。    public AutoResetEvent(bool initialState);}

View Code

选用伊芙ntWaitHandle子类AutoResetEvent创立线程同步锁,效果和Interlocked的自旋锁同样的,不过成效要慢相当多,然而此间未有抢占成功的线程会阻塞,不会自旋。

澳门新萄京官方网站 43澳门新萄京官方网站 44

class SimpleWaitLock{    private AutoResetEvent m_ResourceInUse;    public SimpleWaitLock()    {        m_ResourceInUse = new AutoResetEvent(true);     }    public void Enter()    {        m_ResourceInUse.WaitOne();    }    public void Leave()    {        m_ResourceInUse.Set();    }}

View Code

AutoResetEvent

澳门新萄京官方网站 45澳门新萄京官方网站 46

//
// 摘要:
//     通知正在等待的线程已发生事件。此类不能被继承。
public sealed class AutoResetEvent : EventWaitHandle
{
    //
    // 摘要:
    //     使用 Boolean 值(指示是否将初始状态设置为终止的)初始化 System.Threading.AutoResetEvent 类的新实例。
    //
    // 参数:
    //   initialState:
    //     若要将初始状态设置为终止,则为 true;若要将初始状态设置为非终止,则为 false。
    public AutoResetEvent(bool initialState);
}

View Code

 使用EventWaitHandle子类AutoResetEvent成立线程同步锁,效果和Interlocked的自旋锁同样的,不过功用要慢非常多,可是这里未有抢占成功的线程会阻塞,不会自旋。

澳门新萄京官方网站 47澳门新萄京官方网站 48

class SimpleWaitLock
{
    private AutoResetEvent m_ResourceInUse;
    public SimpleWaitLock()
    {
        m_ResourceInUse = new AutoResetEvent(true);
     }
    public void Enter()
    {
        m_ResourceInUse.WaitOne();
    }
    public void Leave()
    {
        m_ResourceInUse.Set();
    }
}

View Code

第二部、Nuget上安装Microsoft.AspNet.Identity、Microsoft.AspNet.Identity.Owin

第二部、Nuget上安装Microsoft.AspNet.Identity、Microsoft.AspNet.Identity.Owin

Semaphore

澳门新萄京官方网站 49澳门新萄京官方网站 50

//限制可同时访问某一资源或资源池的线程数。public sealed class Semaphore : WaitHandle{    //     初始化 System.Threading.Semaphore 类的新实例,并指定初始入口数和最大并发入口数,可以选择指定系统信号量对象的名称,    //     指定一个变量来接收指示是否创建了新系统信号量的值,以及指定系统信号量的安全访问控制。    public Semaphore(int initialCount, int maximumCount, string name, out bool createdNew, SemaphoreSecurity semaphoreSecurity);    //     用安全访问权限打开指定名称为信号量。    public static Semaphore OpenExisting(string name, SemaphoreRights rights);    //     用安全访问权限打开指定名称为信号量,并返回指示操作是否成功的值。    public static bool TryOpenExisting(string name, SemaphoreRights rights, out Semaphore result);    //     获取已命名的系统信号量的访问控制安全性。    public SemaphoreSecurity GetAccessControl();    //     退出信号量并返回前一个计数。    public int Release();    //     以指定的次数退出信号量并返回前一个计数。    public int Release(int releaseCount);    //     设置已命名的系统信号量的访问控制安全性。    public void SetAccessControl(SemaphoreSecurity semaphoreSecurity);}

View Code

Semaphore

澳门新萄京官方网站 51澳门新萄京官方网站 52

//限制可同时访问某一资源或资源池的线程数。
public sealed class Semaphore : WaitHandle
{
    //     初始化 System.Threading.Semaphore 类的新实例,并指定初始入口数和最大并发入口数,可以选择指定系统信号量对象的名称,
    //     指定一个变量来接收指示是否创建了新系统信号量的值,以及指定系统信号量的安全访问控制。
    public Semaphore(int initialCount, int maximumCount, string name, out bool createdNew, SemaphoreSecurity semaphoreSecurity);

    //     用安全访问权限打开指定名称为信号量(如果已经存在)。
    public static Semaphore OpenExisting(string name, SemaphoreRights rights);

    //     用安全访问权限打开指定名称为信号量(如果已经存在),并返回指示操作是否成功的值。
    public static bool TryOpenExisting(string name, SemaphoreRights rights, out Semaphore result);

    //     获取已命名的系统信号量的访问控制安全性。
    public SemaphoreSecurity GetAccessControl();
    //     退出信号量并返回前一个计数。

    public int Release();
    //     以指定的次数退出信号量并返回前一个计数。
    public int Release(int releaseCount);

    //     设置已命名的系统信号量的访问控制安全性。
    public void SetAccessControl(SemaphoreSecurity semaphoreSecurity);
}

View Code

内部Microsoft.AspNet.Identity.Owin有依附项,它依据了那多少个包:

中间Microsoft.AspNet.Identity.Owin有依据项,它依赖了那多少个包:

Mutex

Mutex代表互斥体,和AutoReset伊芙nt和计数为一的Semaphore相似,三者都是贰次只释放贰个正值班守护候的线程。

澳门新萄京官方网站 53澳门新萄京官方网站 54

//// 摘要://     还可用于进程间同步的同步基元。public sealed class Mutex : WaitHandle{    //     使用可指示调用线程是否应具有互斥体的初始所有权以及字符串是否为互斥体的名称的 Boolean     //     值和当线程返回时可指示调用线程是否已赋予互斥体的初始所有权以及访问控制安全是否已应用到命名互斥体的    //     Boolean 变量初始化 System.Threading.Mutex 类的新实例。    public Mutex(bool initiallyOwned, string name, out bool createdNew, MutexSecurity mutexSecurity);    //     利用所需的安全访问权限,打开指定的已命名的互斥体。    public static Mutex OpenExisting(string name, MutexRights rights);    //     利用所需的安全访问权限,打开指定的已命名的互斥体,并返回指示操作是否成功的值。    public static bool TryOpenExisting(string name, MutexRights rights, out Mutex result);    //     获取表示已命名的互斥体的访问控制安全性的 System.Security.AccessControl.MutexSecurity 对象。    public MutexSecurity GetAccessControl();    //     释放 System.Threading.Mutex 一次。    public void ReleaseMutex();    //     设置已命名的系统互斥体的访问控制安全性。    public void SetAccessControl(MutexSecurity mutexSecurity);}

View Code

Mutex协助递归,当调用M1时得到Mutext推行了线程安全的操作,调用M2时,如故能够试行线程安全的操作,此时假使是AutoResetEvent对象就能在M2方法中梗阻。

澳门新萄京官方网站 55澳门新萄京官方网站 56

class SomeClass : IDisposable{    private readonly Mutex m_lock = new Mutex();    public void Dispose()    {        m_lock.Dispose();    }    public void M1()    {        m_lock.WaitOne();        //do somethine safe        M2();        m_lock.ReleaseMutex();    }    public void M2()    {        m_lock.WaitOne();        //do something safe        m_lock.ReleaseMutex();    }}

View Code

AutoResetEvent也足以达成递归锁,功用比Mutex高

澳门新萄京官方网站 57澳门新萄京官方网站 58

class RecurisveAutoResetEvent : IDisposable{    private AutoResetEvent m_lock = new AutoResetEvent(true);    private int m_owningThread = 0;    private int m_recursionCount = 0;    public void Dispose()    {        m_lock.Dispose();    }    public void Enter()    {        int currentThreadId = Thread.CurrentThread.ManagedThreadId;        //如果调用线程拥有锁,就再递归一次        if(m_owningThread == currentThreadId)        {            m_recursionCount  ;            return;        }        //没有锁,就等待它        m_lock.WaitOne();        //调用线程拥有了锁,初始化线程id和计数        m_owningThread = currentThreadId;        m_recursionCount = 1;    }    public void Leave()    {        //如果调用线程没有锁,就出错了        if(m_owningThread != Thread.CurrentThread.ManagedThreadId)        {            throw new InvalidOperationException();        }        //递归数减一        if(--m_recursionCount == 0)        {            //如果为0,表名该线程没有锁了            m_owningThread = 0;            //唤醒等待的另一个线程            m_lock.Set();        }    }}

View Code

Mutex

 Mutex代表互斥体,和AutoResetEvent和计数为一的Semaphore相似,三者都以贰遍只释放贰个正在等候的线程。

澳门新萄京官方网站 59澳门新萄京官方网站 60

//
// 摘要:
//     还可用于进程间同步的同步基元。
public sealed class Mutex : WaitHandle
{
    //     使用可指示调用线程是否应具有互斥体的初始所有权以及字符串是否为互斥体的名称的 Boolean 
    //     值和当线程返回时可指示调用线程是否已赋予互斥体的初始所有权以及访问控制安全是否已应用到命名互斥体的
    //     Boolean 变量初始化 System.Threading.Mutex 类的新实例。
    public Mutex(bool initiallyOwned, string name, out bool createdNew, MutexSecurity mutexSecurity);

    //     利用所需的安全访问权限,打开指定的已命名的互斥体(如果已经存在)。
    public static Mutex OpenExisting(string name, MutexRights rights);

    //     利用所需的安全访问权限,打开指定的已命名的互斥体(如果已经存在),并返回指示操作是否成功的值。
    public static bool TryOpenExisting(string name, MutexRights rights, out Mutex result);

    //     获取表示已命名的互斥体的访问控制安全性的 System.Security.AccessControl.MutexSecurity 对象。
    public MutexSecurity GetAccessControl();

    //     释放 System.Threading.Mutex 一次。
    public void ReleaseMutex();

    //     设置已命名的系统互斥体的访问控制安全性。
    public void SetAccessControl(MutexSecurity mutexSecurity);
}

View Code

 Mutex帮助递归,当调用M1时获得Mutext实践了线程安全的操作,调用M2时,依然能够实践线程安全的操作,此时只假设AutoReset伊芙nt对象就能在M2方法中梗阻。

澳门新萄京官方网站 61澳门新萄京官方网站 62

class SomeClass : IDisposable
{
    private readonly Mutex m_lock = new Mutex();

    public void Dispose()
    {
        m_lock.Dispose();
    }

    public void M1()
    {
        m_lock.WaitOne();
        //do somethine safe
        M2();
        m_lock.ReleaseMutex();
    }
    public void M2()
    {
        m_lock.WaitOne();
        //do something safe
        m_lock.ReleaseMutex();
    }
}

View Code

 AutoReset伊夫nt也可以兑现递归锁,功能比Mutex高

澳门新萄京官方网站 63澳门新萄京官方网站 64

class RecurisveAutoResetEvent : IDisposable
{
    private AutoResetEvent m_lock = new AutoResetEvent(true);
    private int m_owningThread = 0;
    private int m_recursionCount = 0;

    public void Dispose()
    {
        m_lock.Dispose();
    }

    public void Enter()
    {
        int currentThreadId = Thread.CurrentThread.ManagedThreadId;
        //如果调用线程拥有锁,就再递归一次
        if(m_owningThread == currentThreadId)
        {
            m_recursionCount  ;
            return;
        }
        //没有锁,就等待它
        m_lock.WaitOne();

        //调用线程拥有了锁,初始化线程id和计数
        m_owningThread = currentThreadId;
        m_recursionCount = 1;
    }
    public void Leave()
    {
        //如果调用线程没有锁,就出错了
        if(m_owningThread != Thread.CurrentThread.ManagedThreadId)
        {
            throw new InvalidOperationException();
        }
        //递归数减一
        if(--m_recursionCount == 0)
        {
            //如果为0,表名该线程没有锁了
            m_owningThread = 0;
            //唤醒等待的另一个线程
            m_lock.Set();
        }
    }
}

View Code

Microsoft.Owin.Security.OAuth      MSDN注明:包含与 OAuth 提供程序相关的连串。(详细消息参照他事他说加以考察 )

Microsoft.Owin.Security.OAuth      MSDN评释:富含与 OAuth 提供程序相关的项目。(详细音讯参谋 )

线程同步之混合情势

从不线程竞争时,混合情势提供了客户方式抱有具备的质量优势,有竞争时,混合形式选择了根本方式提供不自旋的优势(制止浪费CPU时间)

线程同步之混合形式

 未有线程竞争时,混合形式提供了客商方式抱有具备的天性优势,有竞争时,混合形式采纳了基石情势提供不自旋的优势(幸免浪费CPU时间)

Microsoft.Owin.Security.Cookies   MSDN评释:提供与身份 cookie 相关的等级次序。     (详细音信参考 )

Microsoft.Owin.Security.Cookies   MSDN评释:提供与身份 cookie 相关的类型。     (详细消息参谋 )

利用Interlocked和AutoResetEvent自定义混合锁

澳门新萄京官方网站 65澳门新萄京官方网站 66

class SimpleHybridLock : IDisposable{    private int m_waiters = 0;    private AutoResetEvent m_waiterLock = new AutoResetEvent(false);    public void Dispose()    {        m_waiterLock.Dispose();    }    public void Enter()    {        //这个线程想要获得锁        if(Interlocked.Increment(ref m_waiters) == 1)        {//无竞争,获得成功            return;        }        //获得失败,没有自旋        //而是使线程等待        m_waiterLock.WaitOne();        //到这里线程拿到了锁    }    public void Leave()    {        //这个线程想要释放锁        if (Interlocked.Decrement(ref m_waiters) == 0)        {            //没有其他线程等待,直接返回            return;        }        //有其他线程正在阻塞,唤醒其中一个        m_waiterLock.Set();    }}

View Code

平等用事先的次序开展测验。显著要比单纯的内核格局要快相当多,可是由于调换内核情势导致品质的损失,结果恐怕比可是的顾客形式慢比比较多。

和:1642668640

用时:11855

请按大肆键继续...

动用Interlocked和AutoReset伊芙nt自定义混合锁

澳门新萄京官方网站 67澳门新萄京官方网站 68

class SimpleHybridLock : IDisposable
{
    private int m_waiters = 0;
    private AutoResetEvent m_waiterLock = new AutoResetEvent(false);

    public void Dispose()
    {
        m_waiterLock.Dispose();
    }

    public void Enter()
    {
        //这个线程想要获得锁
        if(Interlocked.Increment(ref m_waiters) == 1)
        {//无竞争,获得成功
            return;
        }
        //获得失败,没有自旋
        //而是使线程等待
        m_waiterLock.WaitOne();
        //到这里线程拿到了锁
    }
    public void Leave()
    {
        //这个线程想要释放锁
        if (Interlocked.Decrement(ref m_waiters) == 0)
        {
            //没有其他线程等待,直接返回
            return;
        }
        //有其他线程正在阻塞,唤醒其中一个
        m_waiterLock.Set();
    }
}

View Code

 同样用此前的次序进行测量试验。分明要比然则的基业情势要快非常多,不过由于转变内核形式造成品质的损失,结果只怕比独有的客商格局慢非常多。

和:1642668640

用时:11855

请按任性键继续. . .

Microsoft.Owin.Security                 MSDN注脚:包涵与身份验证相关的门类。         (详细新闻参照他事他说加以考察 )

Microsoft.Owin.Security                 MSDN声明:包括与身份验证相关的等级次序。         (详细音讯参照他事他说加以考察 )

自旋片刻的混合锁

因而地方的例子我们精晓,转变内核形式会导致巨大的属性损失。而常见线程占领锁的时辰都极度短,所以能够让线程自旋一小段时光,再转为内核格局。那样能够幸免,一碰到静态条件就转为内核格局幸免的性子消耗,由于占用时间相当的短,自旋的历程中就大概赢得锁了。

澳门新萄京官方网站 69澳门新萄京官方网站 70

class AnotherHybridLock : IDisposable{    //用户模式    private int m_waiters = 0;    //内核模式    private AutoResetEvent m_waiterLock = new AutoResetEvent(false);    //控制自旋    private int m_spincount = 4000;    //当前线程,当前线程占用数    private int m_owingThreadId = 0, m_recursion = 0;    public void Dispose()    {        m_waiterLock.Dispose();    }    public void Enter()    {        int currentThreadId = Thread.CurrentThread.ManagedThreadId;        //如果调用线程拥有锁,就再递归一次        if (m_owingThreadId == currentThreadId)        {            m_recursion  ;            return;        }        //尝试获取        SpinWait spinwait = new SpinWait();        for(int i = 0; i < m_spincount; i  )        {            //如果锁可以使用了,这个线程就获得它,设置状态并返回            if (Interlocked.CompareExchange(ref m_waiters, 1, 0) == 0)                goto GotLock;            //给其他线程运行的机会,希望锁会被释放            spinwait.SpinOnce();        }        //自旋结束,仍未获得锁,再试一次        if(Interlocked.Increment(ref m_waiters) > 1)        {            //仍是竞态条件,阻塞            m_waiterLock.WaitOne();            //新来时拥有锁了。。。        }        GotLock:        //获得锁了。。        m_owingThreadId = currentThreadId;        m_recursion = 1;    }    public void Leave()    {        int currentThreadId = Thread.CurrentThread.ManagedThreadId;        if (m_owingThreadId != currentThreadId)        {            throw new InvalidOperationException();        }        //如果这个线程仍然有锁,直接返回        if(--m_recursion > 0)        {            return;        }        //现在没有线程拥有锁        m_owingThreadId = 0;        //如果没有其他线程等待,直接返回        if(Interlocked.Decrement(ref m_waiters) == 0)        {            return;        }        //有线程等待,唤醒一个        m_waiterLock.Set();    }}

View Code

自旋片刻的混合锁

 通过地点的例证大家领略,调换内核方式会招致巨大的品质损失。而平常线程占领锁的光阴都非常的短,所以能够让线程自旋一小段时光,再转为内核方式。那样能够制止,一遇到静态条件就转为内核情势制止的属性消耗,由于占用时间非常短,自旋的长河中就或者获取锁了。

澳门新萄京官方网站 71澳门新萄京官方网站 72

class AnotherHybridLock : IDisposable
{
    //用户模式
    private int m_waiters = 0;
    //内核模式
    private AutoResetEvent m_waiterLock = new AutoResetEvent(false);
    //控制自旋
    private int m_spincount = 4000;
    //当前线程,当前线程占用数
    private int m_owingThreadId = 0, m_recursion = 0;

    public void Dispose()
    {
        m_waiterLock.Dispose();
    }

    public void Enter()
    {
        int currentThreadId = Thread.CurrentThread.ManagedThreadId;
        //如果调用线程拥有锁,就再递归一次
        if (m_owingThreadId == currentThreadId)
        {
            m_recursion  ;
            return;
        }
        //尝试获取
        SpinWait spinwait = new SpinWait();
        for(int i = 0; i < m_spincount; i  )
        {
            //如果锁可以使用了,这个线程就获得它,设置状态并返回
            if (Interlocked.CompareExchange(ref m_waiters, 1, 0) == 0)
                goto GotLock;
            //给其他线程运行的机会,希望锁会被释放
            spinwait.SpinOnce();
        }
        //自旋结束,仍未获得锁,再试一次
        if(Interlocked.Increment(ref m_waiters) > 1)
        {
            //仍是竞态条件,阻塞
            m_waiterLock.WaitOne();
            //新来时拥有锁了。。。
        }
        GotLock:
        //获得锁了。。
        m_owingThreadId = currentThreadId;
        m_recursion = 1;
    }
    public void Leave()
    {
        int currentThreadId = Thread.CurrentThread.ManagedThreadId;

        if (m_owingThreadId != currentThreadId)
        {
            throw new InvalidOperationException();
        }
        //如果这个线程仍然有锁,直接返回
        if(--m_recursion > 0)
        {
            return;
        }
        //现在没有线程拥有锁
        m_owingThreadId = 0;

        //如果没有其他线程等待,直接返回
        if(Interlocked.Decrement(ref m_waiters) == 0)
        {
            return;
        }
        //有线程等待,唤醒一个
        m_waiterLock.Set();
    }
}

View Code

Microsoft.AspNet.Identity.Core      MSDN表明:包罗与治本 ASP.NET Identity 的客商和剧中人物相关的类和接口。

Microsoft.AspNet.Identity.Core      MSDN表明:饱含与管理 ASP.NET Identity 的客户和剧中人物相关的类和接口。

FCL中的混合格局

马努alReset伊夫ntSlim、SemaphoreSlim使用上和水源形式一致,只是都在顾客方式中自旋,并且都以在放生第1回竞争时,创造基础情势对象。并且能够经过CancellationToken举办中断退出。

澳门新萄京官方网站 73澳门新萄京官方网站 74

static void Main(string[] args){    Stopwatch watch = new Stopwatch();    watch.Start();    int sum = 0;    SemaphoreSlim g = new SemaphoreSlim(1);    var t1 = Task.Run =>    {        for (int i = 0; i < 3000000; i  )        {            g.Wait();            sum  = i;            g.Release();        }    });    var t2 = Task.Run =>    {        for (int i = 3000000; i < 5000000; i  )        {            g.Wait();            sum  = i;            g.Release();        }    });            Task.WaitAll;    Console.WriteLine("和:"   sum);    Console.WriteLine("用时:"   watch.ElapsedMilliseconds);}

View Code

和:1642668640

用时:1267

请按大肆键继续...

FCL中的混合格局

ManualReset伊芙ntSlim、SemaphoreSlim使用上和水源方式一致,只是都在客商形式中自旋,并且都以在放生第二回竞争时,创造基础方式对象。並且能够经过CancellationToken举行中断退出。

澳门新萄京官方网站 75澳门新萄京官方网站 76

static void Main(string[] args)
{
    Stopwatch watch = new Stopwatch();
    watch.Start();
    int sum = 0;
    SemaphoreSlim g = new SemaphoreSlim(1);
    var t1 = Task.Run(() =>
    {
        for (int i = 0; i < 3000000; i  )
        {
            g.Wait();
            sum  = i;
            g.Release();
        }
    });
    var t2 = Task.Run(() =>
    {
        for (int i = 3000000; i < 5000000; i  )
        {
            g.Wait();
            sum  = i;
            g.Release();
        }
    });

    Task.WaitAll(t1, t2);
    Console.WriteLine("和:"   sum);
    Console.WriteLine("用时:"   watch.ElapsedMilliseconds);
}

View Code

和:1642668640

用时:1267

请按大肆键继续. . .

(音讯消息参照他事他说加以考察:)

(音讯音信参照他事他说加以考察:)

Monitor

保存在堆上的指标都有贰个同步索引块,暗中认可意况下同步索引块值为1,何况CLPAJERO最早化时在堆中分红二个联袂数组,调用Monitor.Enter方法时,要是指标同步索引块为1,那么CLKoleos在数组中找到贰个空白块,并安装对象的同步块索引,让它引用该联合块,调用Exit方法时,会检讨是或不是有另外线程正在等待使用这几个合伙块,未有的话,就把同步索引块重新安装为1,有的话就让等待的线程进恢复生机。Enter的第三个参数表示情状,使用的时候,设置贰个暗中认可的false,假设竞态成功,那么就改为true.

澳门新萄京官方网站 77澳门新萄京官方网站 78

public static class Monitor{    public static void Enter(object obj);    public static void Enter(object obj, ref bool lockTaken);    public static void Exit(object obj);    public static bool IsEntered(object obj);    public static void Pulse(object obj);    public static void PulseAll(object obj);    public static void TryEnter(object obj, int millisecondsTimeout, ref bool lockTaken);    public static bool Wait(object obj, int millisecondsTimeout, bool exitContext);}private static readonly object ObjAsync = new object();static void Main(string[] args){    Stopwatch watch = new Stopwatch();    watch.Start();    int sum = 0;    var t1 = Task.Run =>    {        for (int i = 0; i < 3000000; i  )        {            Monitor.Enter;                sum  = i;            Monitor.Exit;        }    });    var t2 = Task.Run =>    {        for (int i = 3000000; i < 5000000; i  )        {            Monitor.Enter;            sum  = i;            Monitor.Exit;        }    });    Task.WaitAll;    Console.WriteLine("和:"   sum);    Console.WriteLine("用时:"   watch.ElapsedMilliseconds);}

View Code

和:1642668640

用时:337

请按任意键继续...

Monitor

 保存在堆上的对象都有二个同步索引块,暗许情形下同步索引块值为1,而且CL大切诺基初阶化时在堆中分红五个联机数组,调用Monitor.Enter方法时,即使指标同步索引块为1(不然,等待),那么CLCRUISER在数组中找到三个空白块,并设置对象的二头块索引,让它引用该联合块,调用Exit方法时,会检讨是否有任何线程正在等待使用这一个合伙块,未有的话,就把同步索引块重新安装为1,有的话就让等待的线程进复苏。Enter的第一个参数表示景况,使用的时候,设置多个暗中认可的false,假诺竞态成功,那么就改为true.

澳门新萄京官方网站 79澳门新萄京官方网站 80

public static class Monitor
{
    public static void Enter(object obj);
    public static void Enter(object obj, ref bool lockTaken);
    public static void Exit(object obj);
    public static bool IsEntered(object obj);
    public static void Pulse(object obj);
    public static void PulseAll(object obj);
    public static void TryEnter(object obj, int millisecondsTimeout, ref bool lockTaken);
    public static bool Wait(object obj, int millisecondsTimeout, bool exitContext);
}
private static readonly object ObjAsync = new object();
static void Main(string[] args)
{
    Stopwatch watch = new Stopwatch();
    watch.Start();
    int sum = 0;
    var t1 = Task.Run(() =>
    {
        for (int i = 0; i < 3000000; i  )
        {
            Monitor.Enter(ObjAsync);
                sum  = i;
            Monitor.Exit(ObjAsync);

        }
    });
    var t2 = Task.Run(() =>
    {
        for (int i = 3000000; i < 5000000; i  )
        {
            Monitor.Enter(ObjAsync);
            sum  = i;
            Monitor.Exit(ObjAsync);

        }
    });
    Task.WaitAll(t1, t2);
    Console.WriteLine("和:"   sum);
    Console.WriteLine("用时:"   watch.ElapsedMilliseconds);
}

View Code

和:1642668640

用时:337

请按任性键继续. . .

从MSDN的注释能够看出来Microsoft.AspNet.Identity.Owin里实际便是将网址的报到、注册专门的学业场景所需的API举办了包装;

从MSDN的注明能够看出来Microsoft.AspNet.Identity.Owin里实际就是将网址的报到、注册工作场景所需的API举办了包装;

里德rWriterLockSlim读写锁分离

ReaderWriterLockSlim用来提供读写锁分离,后面介绍的锁,多个线程同不时间访问分享数据时,线程竞争失利是都会卡住,造成应用程序的伸缩性和吞吐能力下滑。借使持有线程只以读的艺术访谈数据,根本没有需求阻塞他们,应该允许出现的走访数据。

澳门新萄京官方网站 81澳门新萄京官方网站 82

public class ReaderWriterLockSlim : IDisposable{    public ReaderWriterLockSlim();    public ReaderWriterLockSlim(LockRecursionPolicy recursionPolicy);    public int WaitingReadCount { get; }    public int RecursiveWriteCount { get; }    public int RecursiveUpgradeCount { get; }    public int RecursiveReadCount { get; }    public int CurrentReadCount { get; }    public LockRecursionPolicy RecursionPolicy { get; }    public bool IsWriteLockHeld { get; }    public bool IsUpgradeableReadLockHeld { get; }    public bool IsReadLockHeld { get; }    public int WaitingUpgradeCount { get; }    public int WaitingWriteCount { get; }    public void Dispose();    public void EnterReadLock();    public void EnterUpgradeableReadLock();    public void EnterWriteLock();    public void ExitReadLock();    public void ExitUpgradeableReadLock();    public void ExitWriteLock();    public bool TryEnterReadLock(int millisecondsTimeout);    public bool TryEnterUpgradeableReadLock(int millisecondsTimeout);    public bool TryEnterWriteLock(int millisecondsTimeout);}

View Code

应用办法。构造函数中假若枚举为SupportsRecursion表示扶助递归

澳门新萄京官方网站 83澳门新萄京官方网站 84

static void Main(string[] args){    Stopwatch watch = new Stopwatch();    watch.Start();    ReaderWriterLockSlim lo = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);    int sum = 0;    var t1 = Task.Run =>    {        for (int i = 0; i < 3000000; i  )        {            lo.EnterWriteLock();                sum  = i;            lo.ExitWriteLock();        }    });    var t2 = Task.Run =>    {        for (int i = 3000000; i < 5000000; i  )        {            lo.EnterWriteLock();            sum  = i;            lo.ExitWriteLock();        }    });    Task.WaitAll;    Console.WriteLine("和:"   sum);    Console.WriteLine("用时:"   watch.ElapsedMilliseconds);}

View Code

和:1642668640

用时:622

请按任性键继续...

ReaderWriterLockSlim读写锁分离

ReaderWriterLockSlim用来提供读写锁分离,前边介绍的锁,七个线程同一时间访谈分享数据时,线程竞争战败是都会卡住,变成应用程序的紧缩性和吞吐本领下滑。假如持有线程只以读的不二法门访谈数据,根本无需阻塞他们,应该允许出现的拜谒数据。

澳门新萄京官方网站 85澳门新萄京官方网站 86

public class ReaderWriterLockSlim : IDisposable
{
    public ReaderWriterLockSlim();
    public ReaderWriterLockSlim(LockRecursionPolicy recursionPolicy);

    public int WaitingReadCount { get; }
    public int RecursiveWriteCount { get; }
    public int RecursiveUpgradeCount { get; }
    public int RecursiveReadCount { get; }
    public int CurrentReadCount { get; }
    public LockRecursionPolicy RecursionPolicy { get; }
    public bool IsWriteLockHeld { get; }
    public bool IsUpgradeableReadLockHeld { get; }
    public bool IsReadLockHeld { get; }
    public int WaitingUpgradeCount { get; }
    public int WaitingWriteCount { get; }

    public void Dispose();
    public void EnterReadLock();
    public void EnterUpgradeableReadLock();
    public void EnterWriteLock();
    public void ExitReadLock();
    public void ExitUpgradeableReadLock();
    public void ExitWriteLock();
    public bool TryEnterReadLock(int millisecondsTimeout);
    public bool TryEnterUpgradeableReadLock(int millisecondsTimeout);
    public bool TryEnterWriteLock(int millisecondsTimeout);

}

View Code

 使用方法。构造函数中一经枚举为SupportsRecursion代表帮衬递归

澳门新萄京官方网站 87澳门新萄京官方网站 88

static void Main(string[] args)
{
    Stopwatch watch = new Stopwatch();
    watch.Start();

    ReaderWriterLockSlim lo = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
    int sum = 0;
    var t1 = Task.Run(() =>
    {
        for (int i = 0; i < 3000000; i  )
        {
            lo.EnterWriteLock();
                sum  = i;
            lo.ExitWriteLock();

        }
    });
    var t2 = Task.Run(() =>
    {
        for (int i = 3000000; i < 5000000; i  )
        {
            lo.EnterWriteLock();
            sum  = i;
            lo.ExitWriteLock();

        }
    });
    Task.WaitAll(t1, t2);
    Console.WriteLine("和:"   sum);
    Console.WriteLine("用时:"   watch.ElapsedMilliseconds);
}

View Code

和:1642668640

用时:622

请按任性键继续. . .

第三部、建模

第三部、建模

双锁本事

或然会有人感到怎么lock外已经推断了_instance不为null了,为啥还要剖断?

莫不会有四个线程同偶尔候经过第三个if剖断,然后只有二个线程进入了lock内部,创设达成没不平常,可是原先等待的线程就步向了,如若尚未第三个if,他就更创办了一个对象!!!

澳门新萄京官方网站 89澳门新萄京官方网站 90

class Singleton{    private static object s_lock = new object();    private static Singleton _instance = null;    private Singleton() { }    public static Singleton GetSingleTon()    {        if (_instance != null) return _instance;        lock         {            if(_instance == null)            {                Singleton temp = new Singleton();                Volatile.Write(ref _instance, temp);            }        }        return _instance;    }}

View Code

双锁技能(单例方式)

 可能会有人感到怎么lock外已经决断了_instance不为null了,为啥还要剖断?

或然会有七个线程同有时间经过第二个if剖断,然后独有叁个线程踏入了lock内部,创建完毕没极度,可是原先等待的线程就进来了,假若未有第三个if,他就再次创下办了二个指标!!!

澳门新萄京官方网站 91澳门新萄京官方网站 92

class Singleton
{
    private static object s_lock = new object();

    private static Singleton _instance = null;

    private Singleton() { }

    public static Singleton GetSingleTon()
    {
        if (_instance != null) return _instance;

        lock (s_lock)
        {
            if(_instance == null)
            {
                Singleton temp = new Singleton();
                Volatile.Write(ref _instance, temp);
            }
        }
        return _instance;
    }
}

View Code

如自己以后的数据库的客户表为BASE_USE传祺,表结构如下

如自个儿以往的数据库的客商表为BASE_USEPRADO,表结构如下

小结

这一节学习了.net中的各样锁,Monitor肯定时行使的最多的,不过有个别时候,大概须要具体深入分析吧,例如在MVC中的路由的读取,由于路由是前后相继运维的时候开头化的(只有一遍),所以利用了ReaderWriterLockSlim来提用读写锁分离的情势,又比如MVC中封装的AsyncResultWrapper是采纳Monitor和Interlocked结合的方法来举办线程同步的。这一篇真的好长啊。鼓舞一下和煦,加油,嘿嘿!!

小结

这一节学习了.net中的各类锁,Monitor确按期采取的最多的,不过多少时候,也许需求具体解析吧,举例在MVC中的路由的读取,由于路由是程序运维的时候开首化的(独有一遍),所以使用了ReaderWriterLockSlim来提用读写锁分离的形式,又比方说MVC中封装的AsyncResultWrapper是运用Monitor和Interlocked结合的艺术来扩充线程同步的。这一篇真的好长啊。激励一下团结,加油,嘿嘿!!

 

 

 

 

CREATE TABLE [dbo].[BASE_USER](
    [ID] [uniqueidentifier] NOT NULL PRIMARY KEY,
    [NAME] [varchar](50) NOT NULL,
    [PWD] [varchar](50) NOT NULL,
) ON [PRIMARY]
CREATE TABLE [dbo].[BASE_USER](
    [ID] [uniqueidentifier] NOT NULL PRIMARY KEY,
    [NAME] [varchar](50) NOT NULL,
    [PWD] [varchar](50) NOT NULL,
) ON [PRIMARY]

  

  

咱俩在工程站点的Models文件夹里新建三个BASE_USE安德拉类,让它一而再Microsoft.AspNet.Identity.IUser<GUID>,这里我们加三个数据表不真实的NICKNAME小名字段,到末端看看会有怎么着意义~

我们在工程站点的Models文件夹里新建一个BASE_USEEnclave类,让它继续Microsoft.AspNet.Identity.IUser<GUID>,这里大家加贰个数据表不设有的NICKNAME小名字段,到背后看看会有何样坚守~

澳门新萄京官方网站 93澳门新萄京官方网站 94

澳门新萄京官方网站 95澳门新萄京官方网站 96

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Web;
 5 
 6 namespace IdeintityDemo.Models
 7 {
 8     public class BASE_USER : Microsoft.AspNet.Identity.IUser<Guid>
 9     {
10         /// <summary>
11         /// 用户编号
12         /// </summary>
13         public Guid Id { get; set; }
14         /// <summary>
15         /// 用户名
16         /// </summary>
17         public string UserName { get; set; }
18         /// <summary>
19         /// 密码
20         /// </summary>
21         public string PWD { get; set; }
22         /// <summary>
23         /// 昵称
24         /// </summary>
25         public string NickName { get; set; }
26 
27         public bool RememberMe { get; set; }
28     }
29 }
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Web;
 5 
 6 namespace IdeintityDemo.Models
 7 {
 8     public class BASE_USER : Microsoft.AspNet.Identity.IUser<Guid>
 9     {
10         /// <summary>
11         /// 用户编号
12         /// </summary>
13         public Guid Id { get; set; }
14         /// <summary>
15         /// 用户名
16         /// </summary>
17         public string UserName { get; set; }
18         /// <summary>
19         /// 密码
20         /// </summary>
21         public string PWD { get; set; }
22         /// <summary>
23         /// 昵称
24         /// </summary>
25         public string NickName { get; set; }
26 
27         public bool RememberMe { get; set; }
28     }
29 }

View Code

View Code

 

 

第四部 创立UserStore类,该类通过持续接口IUserStore来达成客商存款和储蓄在数据库的api

第四部 创立UserStore类,该类通过持续接口IUserStore来完成客户存款和储蓄在数据库的api

澳门新萄京官方网站 97澳门新萄京官方网站 98

澳门新萄京官方网站 99澳门新萄京官方网站 100

  1 using Microsoft.AspNet.Identity;
  2 using System;
  3 using System.Collections.Generic;
  4 using System.Data;
  5 using System.Data.SqlClient;
  6 using System.Linq;
  7 using System.Threading.Tasks;
  8 using System.Web;
  9 using System.Security.Claims;
 10 using IdeintityDemo.Models;
 11 using IdeintityDemo.Common;
 12 
 13 namespace IdeintityDemo.Identity
 14 {
 15 
 16     /// <summary>
 17     /// 用户持久化存储对象
 18     /// 必须实现Microsoft.AspNet.Identity相应接口,否则在SignInManager类进行登录校验过程中
 19     /// 会弹出未实现相关接口的异常!   
 20     /// IUserStore:检测是否存在账户
 21     /// IUserPasswordStore:校验密码
 22     /// IUserLockoutStore:锁定账户相关操作
 23     /// IUserClaimStore:存储用户特定的声明
 24     /// IUserEmailStore:邮箱关联、验证等相关操作
 25     /// IUserPhoneNumberStore:手机关联、验证相关操作
 26     /// IUserTwoFactorStore:获取或设置用户双重身份验证的方法。
 27     /// </summary>
 28     public class HsUserStore : Microsoft.AspNet.Identity.IUserStore<BASE_USER, Guid>,
 29                                Microsoft.AspNet.Identity.IUserPasswordStore<BASE_USER, Guid>,
 30                                Microsoft.AspNet.Identity.IUserLockoutStore<BASE_USER, Guid>,
 31                                Microsoft.AspNet.Identity.IUserClaimStore<BASE_USER, Guid>,
 32                                Microsoft.AspNet.Identity.IUserEmailStore<BASE_USER, Guid>,
 33                                Microsoft.AspNet.Identity.IUserPhoneNumberStore<BASE_USER, Guid>,
 34                                Microsoft.AspNet.Identity.IUserTwoFactorStore<BASE_USER, Guid>
 35     {
 36 
 37         /// <summary>
 38         /// 声明
 39         /// </summary>
 40         public IList<System.Security.Claims.Claim> Claims = null;
 41         /// <summary>
 42         /// 用户
 43         /// </summary>
 44         public BASE_USER UserIdentity = null;
 45 
 46         /// <summary>
 47         /// 实例化
 48         /// </summary>
 49         public HsUserStore()
 50         {
 51             //声明
 52             Claims = new List<System.Security.Claims.Claim>();
 53         }
 54         /// <summary>
 55         /// 创建用户
 56         /// </summary>
 57         /// <param name="user"></param>
 58         /// <returns></returns>
 59         public Task CreateAsync(BASE_USER user)
 60         {
 61             return Task.Run(() => {
 62                 string sql = @"INSERT INTO [dbo].[BASE_USER]([ID],[NAME],[PWD])
 63                         VALUES(@UserID,@name,@pwd)";
 64                 SqlParameter[] parameters = {
 65                      new SqlParameter("@UserID", Guid.NewGuid()),
 66                      new SqlParameter("@name", user.UserName),
 67                      new SqlParameter("@pwd", user.PWD)
 68                 };
 69                 int iResult = DbHelperSQL.ExecuteSql(sql, parameters);
 70             });
 71         }
 72         /// <summary>
 73         /// 删除用户
 74         /// </summary>
 75         /// <param name="user"></param>
 76         /// <returns></returns>
 77         public Task DeleteAsync(BASE_USER user)
 78         {
 79             return Task.Run(() => {
 80                 string sql = @"DELETE FROM [dbo].[BASE_USER] WHERE ID=@ID";
 81                 SqlParameter[] parameters = {
 82                      new SqlParameter("@UserID", user.Id)};
 83                 int iResult = DbHelperSQL.ExecuteSql(sql, parameters);
 84             });
 85         }
 86         /// <summary>
 87         /// 根据用户id获取用户
 88         /// </summary>
 89         /// <param name="userId"></param>
 90         /// <returns></returns>
 91         public Task<BASE_USER> FindByIdAsync(Guid userId)
 92         {
 93             return Task<BASE_USER>.Run(() =>
 94              {
 95                  BASE_USER result = new BASE_USER();
 96                  string sql = @"SELECT * FROM [dbo].[BASE_USER] WHERE ID=@ID";
 97                  SqlParameter[] parameters = {
 98                      new SqlParameter("@ID", userId)};
 99                  DataSet ds = DbHelperSQL.Query(sql, parameters);
100                  if (ds == null || ds.Tables == null || ds.Tables[0].Rows.Count <= 0)
101                      return result;
102                  //model
103                  DataRow dr = ds.Tables[0].Rows[0];
104                  result.Id = Guid.Parse(dr["ID"].ToString());
105                  result.UserName = dr["NAME"].ToString();
106                  result.PWD = dr["PWD"].ToString();
107                  return result;
108              });
109         }
110         /// <summary>
111         /// 根据名称获取用户信息
112         /// </summary>
113         /// <param name="userName"></param>
114         /// <returns></returns>
115         public Task<BASE_USER> FindByNameAsync(string userName)
116         {
117             return Task<BASE_USER>.Run(() =>
118             {
119                 BASE_USER result = new BASE_USER();
120                 string sql = @"SELECT * FROM [dbo].[BASE_USER] WHERE NAME=@NAME";
121                 SqlParameter[] parameters = {
122                      new SqlParameter("@NAME", userName)};
123                 DataSet ds = DbHelperSQL.Query(sql, parameters);
124                 if (ds == null || ds.Tables == null || ds.Tables[0].Rows.Count <= 0)
125                     return result;
126                 //model
127                 DataRow dr = ds.Tables[0].Rows[0];
128                 result.Id = Guid.Parse(dr["ID"].ToString());
129                 result.UserName = dr["NAME"].ToString();
130                 result.PWD = dr["PWD"].ToString();
131 
132                 return result;
133             });
134         }
135         /// <summary>
136         /// 更新用户
137         /// </summary>
138         /// <param name="user"></param>
139         /// <returns></returns>
140         public Task UpdateAsync(BASE_USER user)
141         {
142             return Task.Run(() =>
143             {
144                 //省略...
145             });
146         }
147         /// <summary>
148         /// 异步返回当前失败的访问尝试次数。当密码被验证或帐户被锁定时,这个数字通常会被重置。
149         /// (这里因为我数据库里没有去做这一块的记录保存,所以先写死返回1)
150         /// </summary>
151         /// <param name="user">用户</param>
152         /// <returns></returns>
153         public Task<int> GetAccessFailedCountAsync(BASE_USER user)
154         {
155             return Task.FromResult<int>(1);
156         }
157         /// <summary>
158         /// 获取锁定状态
159         /// </summary>
160         /// <param name="user"></param>
161         /// <returns></returns>
162         public Task<bool> GetLockoutEnabledAsync(BASE_USER user)
163         {
164             return Task.FromResult<bool>(false);
165         }
166         /// <summary>
167         /// 获取锁定结束时间
168         /// </summary>
169         /// <param name="user"></param>
170         /// <returns></returns>
171         public Task<DateTimeOffset> GetLockoutEndDateAsync(BASE_USER user)
172         {
173             throw new NotImplementedException();
174         }
175         /// <summary>
176         /// 记录试图访问用户失败的记录。
177         /// </summary>
178         /// <param name="user"></param>
179         /// <returns></returns>
180         public Task<int> IncrementAccessFailedCountAsync(BASE_USER user)
181         {
182             return Task.FromResult<int>(1);
183         }
184         /// <summary>
185         /// 重置访问失败计数,通常在帐户成功访问之后
186         /// </summary>
187         /// <param name="user"></param>
188         /// <returns></returns>
189         public Task ResetAccessFailedCountAsync(BASE_USER user)
190         {
191             return Task.FromResult(false);
192         }
193         /// <summary>
194         /// 异步设置是否可以锁定用户。
195         /// </summary>
196         /// <param name="user"></param>
197         /// <param name="enabled"></param>
198         /// <returns></returns>
199         public Task SetLockoutEnabledAsync(BASE_USER user, bool enabled)
200         {
201             return Task.Run(() => { });
202         }
203         /// <summary>
204         /// 异步锁定用户直到指定的结束日期
205         /// </summary>
206         /// <param name="user"></param>
207         /// <param name="lockoutEnd"></param>
208         /// <returns></returns>
209         public Task SetLockoutEndDateAsync(BASE_USER user, DateTimeOffset lockoutEnd)
210         {
211             return Task.Run(() =>
212             {
213 
214             });
215         }
216         /// <summary>
217         /// 获取用户密码
218         /// </summary>
219         /// <param name="user"></param>
220         /// <returns></returns>
221         public Task<string> GetPasswordHashAsync(BASE_USER user)
222         {
223             return Task<string>.Run(() =>
224             {
225                 return user.PWD;
226             });
227         }
228         /// <summary>
229         /// 是否有密码
230         /// </summary>
231         /// <param name="user"></param>
232         /// <returns></returns>
233         public Task<bool> HasPasswordAsync(BASE_USER user)
234         {
235             return Task.FromResult<bool>(!string.IsNullOrEmpty(user.PWD));
236         }
237         /// <summary>
238         /// 密码进行加密
239         /// </summary>
240         /// <param name="user"></param>
241         /// <param name="passwordHash"></param>
242         /// <returns></returns>
243         public Task SetPasswordHashAsync(BASE_USER user, string passwordHash)
244         {
245             return Task.Run(() =>
246             {
247                 user.PWD = passwordHash;//加密后
248             });
249         }
250         /// <summary>
251         /// 添加一个声明
252         /// </summary>
253         /// <param name="user"></param>
254         /// <param name="claim"></param>
255         /// <returns></returns>
256         public Task AddClaimAsync(BASE_USER user, Claim claim)
257         {
258             return Task.Run(() => { Claims.Add(claim); });
259         }
260         /// <summary>
261         /// 获取改用户的所有声明
262         /// </summary>
263         /// <param name="user"></param>
264         /// <returns></returns>
265         public Task<IList<Claim>> GetClaimsAsync(BASE_USER user)
266         {
267             return Task.Run<IList<System.Security.Claims.Claim>>(() =>
268             {
269                 IList<System.Security.Claims.Claim> list = new List<System.Security.Claims.Claim>();
270                 return list;
271             });
272         }
273         /// <summary>
274         /// 移除申明
275         /// </summary>
276         /// <param name="user"></param>
277         /// <param name="claim"></param>
278         /// <returns></returns>
279         public Task RemoveClaimAsync(BASE_USER user, Claim claim)
280         {
281             return Task.Run(() =>
282             {
283 
284             });
285         }
286         /// <summary>
287         /// 通过邮箱获取对应的用户信息
288         /// </summary>
289         /// <param name="email"></param>
290         /// <returns></returns>
291         public Task<BASE_USER> FindByEmailAsync(string email)
292         {
293             return Task<BASE_USER>.Run(() => new BASE_USER());
294         }
295         /// <summary>
296         /// 获取邮箱
297         /// </summary>
298         /// <param name="user"></param>
299         /// <returns></returns>
300         public Task<string> GetEmailAsync(BASE_USER user)
301         {
302             return Task<string>.Run(() => string.Empty);
303         }
304         /// <summary>
305         /// 确认邮箱
306         /// </summary>
307         /// <param name="user"></param>
308         /// <returns></returns>
309         public Task<bool> GetEmailConfirmedAsync(BASE_USER user)
310         {
311             return Task.FromResult<bool>(true);
312         }
313         /// <summary>
314         /// 修改邮箱
315         /// </summary>
316         /// <param name="user"></param>
317         /// <param name="email"></param>
318         /// <returns></returns>
319         public Task SetEmailAsync(BASE_USER user, string email)
320         {
321             return Task.Run(() => { });
322         }
323         /// <summary>
324         ///设置用户是否邮箱确认 
325         /// </summary>
326         /// <param name="user"></param>
327         /// <param name="confirmed"></param>
328         /// <returns></returns>
329         public Task SetEmailConfirmedAsync(BASE_USER user, bool confirmed)
330         {
331             throw new NotImplementedException();
332         }
333         /// <summary>
334         /// 获取联系电话
335         /// </summary>
336         /// <param name="user"></param>
337         /// <returns></returns>
338         public Task<string> GetPhoneNumberAsync(BASE_USER user)
339         {
340             return Task.FromResult<string>(string.Empty);
341         }
342         /// <summary>
343         /// 获取用户电话号码是否已确认
344         /// </summary>
345         /// <param name="user"></param>
346         /// <returns></returns>
347         public Task<bool> GetPhoneNumberConfirmedAsync(BASE_USER user)
348         {
349             return Task.FromResult<bool>(true);
350         }
351         /// <summary>
352         /// 设置用户电话号码
353         /// </summary>
354         /// <param name="user"></param>
355         /// <param name="phoneNumber"></param>
356         /// <returns></returns>
357         public Task SetPhoneNumberAsync(BASE_USER user, string phoneNumber)
358         {
359             return Task.Run(() => { });
360         }
361         /// <summary>
362         /// 设置与用户关联的电话号码
363         /// </summary>
364         /// <param name="user"></param>
365         /// <param name="confirmed"></param>
366         /// <returns></returns>
367         public Task SetPhoneNumberConfirmedAsync(BASE_USER user, bool confirmed)
368         {
369             return Task.Run(() => { });
370         }
371         /// <summary>
372         /// 是否为用户启用了双重身份验证。
373         /// </summary>
374         /// <param name="user"></param>
375         /// <returns></returns>
376         public Task<bool> GetTwoFactorEnabledAsync(BASE_USER user)
377         {
378             return Task.FromResult<bool>(false);
379         }
380         /// <summary>
381         /// 设置双重身份验证
382         /// </summary>
383         /// <param name="user"></param>
384         /// <param name="enabled"></param>
385         /// <returns></returns>
386         public Task SetTwoFactorEnabledAsync(BASE_USER user, bool enabled)
387         {
388             throw new NotImplementedException();
389         }
390         /// <summary>
391         /// 释放
392         /// </summary>
393         public void Dispose()
394         {
395             throw new NotImplementedException();
396         }
397 
398     }
399 }
  1 using Microsoft.AspNet.Identity;
  2 using System;
  3 using System.Collections.Generic;
  4 using System.Data;
  5 using System.Data.SqlClient;
  6 using System.Linq;
  7 using System.Threading.Tasks;
  8 using System.Web;
  9 using System.Security.Claims;
 10 using IdeintityDemo.Models;
 11 using IdeintityDemo.Common;
 12 
 13 namespace IdeintityDemo.Identity
 14 {
 15 
 16     /// <summary>
 17     /// 用户持久化存储对象
 18     /// 必须实现Microsoft.AspNet.Identity相应接口,否则在SignInManager类进行登录校验过程中
 19     /// 会弹出未实现相关接口的异常!   
 20     /// IUserStore:检测是否存在账户
 21     /// IUserPasswordStore:校验密码
 22     /// IUserLockoutStore:锁定账户相关操作
 23     /// IUserClaimStore:存储用户特定的声明
 24     /// IUserEmailStore:邮箱关联、验证等相关操作
 25     /// IUserPhoneNumberStore:手机关联、验证相关操作
 26     /// IUserTwoFactorStore:获取或设置用户双重身份验证的方法。
 27     /// </summary>
 28     public class HsUserStore : Microsoft.AspNet.Identity.IUserStore<BASE_USER, Guid>,
 29                                Microsoft.AspNet.Identity.IUserPasswordStore<BASE_USER, Guid>,
 30                                Microsoft.AspNet.Identity.IUserLockoutStore<BASE_USER, Guid>,
 31                                Microsoft.AspNet.Identity.IUserClaimStore<BASE_USER, Guid>,
 32                                Microsoft.AspNet.Identity.IUserEmailStore<BASE_USER, Guid>,
 33                                Microsoft.AspNet.Identity.IUserPhoneNumberStore<BASE_USER, Guid>,
 34                                Microsoft.AspNet.Identity.IUserTwoFactorStore<BASE_USER, Guid>
 35     {
 36 
 37         /// <summary>
 38         /// 声明
 39         /// </summary>
 40         public IList<System.Security.Claims.Claim> Claims = null;
 41         /// <summary>
 42         /// 用户
 43         /// </summary>
 44         public BASE_USER UserIdentity = null;
 45 
 46         /// <summary>
 47         /// 实例化
 48         /// </summary>
 49         public HsUserStore()
 50         {
 51             //声明
 52             Claims = new List<System.Security.Claims.Claim>();
 53         }
 54         /// <summary>
 55         /// 创建用户
 56         /// </summary>
 57         /// <param name="user"></param>
 58         /// <returns></returns>
 59         public Task CreateAsync(BASE_USER user)
 60         {
 61             return Task.Run(() => {
 62                 string sql = @"INSERT INTO [dbo].[BASE_USER]([ID],[NAME],[PWD])
 63                         VALUES(@UserID,@name,@pwd)";
 64                 SqlParameter[] parameters = {
 65                      new SqlParameter("@UserID", Guid.NewGuid()),
 66                      new SqlParameter("@name", user.UserName),
 67                      new SqlParameter("@pwd", user.PWD)
 68                 };
 69                 int iResult = DbHelperSQL.ExecuteSql(sql, parameters);
 70             });
 71         }
 72         /// <summary>
 73         /// 删除用户
 74         /// </summary>
 75         /// <param name="user"></param>
 76         /// <returns></returns>
 77         public Task DeleteAsync(BASE_USER user)
 78         {
 79             return Task.Run(() => {
 80                 string sql = @"DELETE FROM [dbo].[BASE_USER] WHERE ID=@ID";
 81                 SqlParameter[] parameters = {
 82                      new SqlParameter("@UserID", user.Id)};
 83                 int iResult = DbHelperSQL.ExecuteSql(sql, parameters);
 84             });
 85         }
 86         /// <summary>
 87         /// 根据用户id获取用户
 88         /// </summary>
 89         /// <param name="userId"></param>
 90         /// <returns></returns>
 91         public Task<BASE_USER> FindByIdAsync(Guid userId)
 92         {
 93             return Task<BASE_USER>.Run(() =>
 94              {
 95                  BASE_USER result = new BASE_USER();
 96                  string sql = @"SELECT * FROM [dbo].[BASE_USER] WHERE ID=@ID";
 97                  SqlParameter[] parameters = {
 98                      new SqlParameter("@ID", userId)};
 99                  DataSet ds = DbHelperSQL.Query(sql, parameters);
100                  if (ds == null || ds.Tables == null || ds.Tables[0].Rows.Count <= 0)
101                      return result;
102                  //model
103                  DataRow dr = ds.Tables[0].Rows[0];
104                  result.Id = Guid.Parse(dr["ID"].ToString());
105                  result.UserName = dr["NAME"].ToString();
106                  result.PWD = dr["PWD"].ToString();
107                  return result;
108              });
109         }
110         /// <summary>
111         /// 根据名称获取用户信息
112         /// </summary>
113         /// <param name="userName"></param>
114         /// <returns></returns>
115         public Task<BASE_USER> FindByNameAsync(string userName)
116         {
117             return Task<BASE_USER>.Run(() =>
118             {
119                 BASE_USER result = new BASE_USER();
120                 string sql = @"SELECT * FROM [dbo].[BASE_USER] WHERE NAME=@NAME";
121                 SqlParameter[] parameters = {
122                      new SqlParameter("@NAME", userName)};
123                 DataSet ds = DbHelperSQL.Query(sql, parameters);
124                 if (ds == null || ds.Tables == null || ds.Tables[0].Rows.Count <= 0)
125                     return result;
126                 //model
127                 DataRow dr = ds.Tables[0].Rows[0];
128                 result.Id = Guid.Parse(dr["ID"].ToString());
129                 result.UserName = dr["NAME"].ToString();
130                 result.PWD = dr["PWD"].ToString();
131 
132                 return result;
133             });
134         }
135         /// <summary>
136         /// 更新用户
137         /// </summary>
138         /// <param name="user"></param>
139         /// <returns></returns>
140         public Task UpdateAsync(BASE_USER user)
141         {
142             return Task.Run(() =>
143             {
144                 //省略...
145             });
146         }
147         /// <summary>
148         /// 异步返回当前失败的访问尝试次数。当密码被验证或帐户被锁定时,这个数字通常会被重置。
149         /// (这里因为我数据库里没有去做这一块的记录保存,所以先写死返回1)
150         /// </summary>
151         /// <param name="user">用户</param>
152         /// <returns></returns>
153         public Task<int> GetAccessFailedCountAsync(BASE_USER user)
154         {
155             return Task.FromResult<int>(1);
156         }
157         /// <summary>
158         /// 获取锁定状态
159         /// </summary>
160         /// <param name="user"></param>
161         /// <returns></returns>
162         public Task<bool> GetLockoutEnabledAsync(BASE_USER user)
163         {
164             return Task.FromResult<bool>(false);
165         }
166         /// <summary>
167         /// 获取锁定结束时间
168         /// </summary>
169         /// <param name="user"></param>
170         /// <returns></returns>
171         public Task<DateTimeOffset> GetLockoutEndDateAsync(BASE_USER user)
172         {
173             throw new NotImplementedException();
174         }
175         /// <summary>
176         /// 记录试图访问用户失败的记录。
177         /// </summary>
178         /// <param name="user"></param>
179         /// <returns></returns>
180         public Task<int> IncrementAccessFailedCountAsync(BASE_USER user)
181         {
182             return Task.FromResult<int>(1);
183         }
184         /// <summary>
185         /// 重置访问失败计数,通常在帐户成功访问之后
186         /// </summary>
187         /// <param name="user"></param>
188         /// <returns></returns>
189         public Task ResetAccessFailedCountAsync(BASE_USER user)
190         {
191             return Task.FromResult(false);
192         }
193         /// <summary>
194         /// 异步设置是否可以锁定用户。
195         /// </summary>
196         /// <param name="user"></param>
197         /// <param name="enabled"></param>
198         /// <returns></returns>
199         public Task SetLockoutEnabledAsync(BASE_USER user, bool enabled)
200         {
201             return Task.Run(() => { });
202         }
203         /// <summary>
204         /// 异步锁定用户直到指定的结束日期
205         /// </summary>
206         /// <param name="user"></param>
207         /// <param name="lockoutEnd"></param>
208         /// <returns></returns>
209         public Task SetLockoutEndDateAsync(BASE_USER user, DateTimeOffset lockoutEnd)
210         {
211             return Task.Run(() =>
212             {
213 
214             });
215         }
216         /// <summary>
217         /// 获取用户密码
218         /// </summary>
219         /// <param name="user"></param>
220         /// <returns></returns>
221         public Task<string> GetPasswordHashAsync(BASE_USER user)
222         {
223             return Task<string>.Run(() =>
224             {
225                 return user.PWD;
226             });
227         }
228         /// <summary>
229         /// 是否有密码
230         /// </summary>
231         /// <param name="user"></param>
232         /// <returns></returns>
233         public Task<bool> HasPasswordAsync(BASE_USER user)
234         {
235             return Task.FromResult<bool>(!string.IsNullOrEmpty(user.PWD));
236         }
237         /// <summary>
238         /// 密码进行加密
239         /// </summary>
240         /// <param name="user"></param>
241         /// <param name="passwordHash"></param>
242         /// <returns></returns>
243         public Task SetPasswordHashAsync(BASE_USER user, string passwordHash)
244         {
245             return Task.Run(() =>
246             {
247                 user.PWD = passwordHash;//加密后
248             });
249         }
250         /// <summary>
251         /// 添加一个声明
252         /// </summary>
253         /// <param name="user"></param>
254         /// <param name="claim"></param>
255         /// <returns></returns>
256         public Task AddClaimAsync(BASE_USER user, Claim claim)
257         {
258             return Task.Run(() => { Claims.Add(claim); });
259         }
260         /// <summary>
261         /// 获取改用户的所有声明
262         /// </summary>
263         /// <param name="user"></param>
264         /// <returns></returns>
265         public Task<IList<Claim>> GetClaimsAsync(BASE_USER user)
266         {
267             return Task.Run<IList<System.Security.Claims.Claim>>(() =>
268             {
269                 IList<System.Security.Claims.Claim> list = new List<System.Security.Claims.Claim>();
270                 return list;
271             });
272         }
273         /// <summary>
274         /// 移除申明
275         /// </summary>
276         /// <param name="user"></param>
277         /// <param name="claim"></param>
278         /// <returns></returns>
279         public Task RemoveClaimAsync(BASE_USER user, Claim claim)
280         {
281             return Task.Run(() =>
282             {
283 
284             });
285         }
286         /// <summary>
287         /// 通过邮箱获取对应的用户信息
288         /// </summary>
289         /// <param name="email"></param>
290         /// <returns></returns>
291         public Task<BASE_USER> FindByEmailAsync(string email)
292         {
293             return Task<BASE_USER>.Run(() => new BASE_USER());
294         }
295         /// <summary>
296         /// 获取邮箱
297         /// </summary>
298         /// <param name="user"></param>
299         /// <returns></returns>
300         public Task<string> GetEmailAsync(BASE_USER user)
301         {
302             return Task<string>.Run(() => string.Empty);
303         }
304         /// <summary>
305         /// 确认邮箱
306         /// </summary>
307         /// <param name="user"></param>
308         /// <returns></returns>
309         public Task<bool> GetEmailConfirmedAsync(BASE_USER user)
310         {
311             return Task.FromResult<bool>(true);
312         }
313         /// <summary>
314         /// 修改邮箱
315         /// </summary>
316         /// <param name="user"></param>
317         /// <param name="email"></param>
318         /// <returns></returns>
319         public Task SetEmailAsync(BASE_USER user, string email)
320         {
321             return Task.Run(() => { });
322         }
323         /// <summary>
324         ///设置用户是否邮箱确认 
325         /// </summary>
326         /// <param name="user"></param>
327         /// <param name="confirmed"></param>
328         /// <returns></returns>
329         public Task SetEmailConfirmedAsync(BASE_USER user, bool confirmed)
330         {
331             throw new NotImplementedException();
332         }
333         /// <summary>
334         /// 获取联系电话
335         /// </summary>
336         /// <param name="user"></param>
337         /// <returns></returns>
338         public Task<string> GetPhoneNumberAsync(BASE_USER user)
339         {
340             return Task.FromResult<string>(string.Empty);
341         }
342         /// <summary>
343         /// 获取用户电话号码是否已确认
344         /// </summary>
345         /// <param name="user"></param>
346         /// <returns></returns>
347         public Task<bool> GetPhoneNumberConfirmedAsync(BASE_USER user)
348         {
349             return Task.FromResult<bool>(true);
350         }
351         /// <summary>
352         /// 设置用户电话号码
353         /// </summary>
354         /// <param name="user"></param>
355         /// <param name="phoneNumber"></param>
356         /// <returns></returns>
357         public Task SetPhoneNumberAsync(BASE_USER user, string phoneNumber)
358         {
359             return Task.Run(() => { });
360         }
361         /// <summary>
362         /// 设置与用户关联的电话号码
363         /// </summary>
364         /// <param name="user"></param>
365         /// <param name="confirmed"></param>
366         /// <returns></returns>
367         public Task SetPhoneNumberConfirmedAsync(BASE_USER user, bool confirmed)
368         {
369             return Task.Run(() => { });
370         }
371         /// <summary>
372         /// 是否为用户启用了双重身份验证。
373         /// </summary>
374         /// <param name="user"></param>
375         /// <returns></returns>
376         public Task<bool> GetTwoFactorEnabledAsync(BASE_USER user)
377         {
378             return Task.FromResult<bool>(false);
379         }
380         /// <summary>
381         /// 设置双重身份验证
382         /// </summary>
383         /// <param name="user"></param>
384         /// <param name="enabled"></param>
385         /// <returns></returns>
386         public Task SetTwoFactorEnabledAsync(BASE_USER user, bool enabled)
387         {
388             throw new NotImplementedException();
389         }
390         /// <summary>
391         /// 释放
392         /// </summary>
393         public void Dispose()
394         {
395             throw new NotImplementedException();
396         }
397 
398     }
399 }

View Code

View Code

 

 

 

 

第五步承袭UserManager类

第五步承接UserManager类

澳门新萄京官方网站 101澳门新萄京官方网站 102

澳门新萄京官方网站 103澳门新萄京官方网站 104

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Web;
 5 using IdeintityDemo.Models;
 6 using Microsoft.AspNet.Identity;
 7 
 8 namespace IdeintityDemo.Identity
 9 {
10     public class HsUserManager:UserManager<BASE_USER,Guid>
11     {
12         /// <summary>
13         /// 通过构造函数注入用户存储实现类
14         /// </summary>
15         /// <param name="store"></param>
16         public HsUserManager(HsUserStore store) : base(store)
17         {
18 
19         }        
20     }
21 }
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Web;
 5 using IdeintityDemo.Models;
 6 using Microsoft.AspNet.Identity;
 7 
 8 namespace IdeintityDemo.Identity
 9 {
10     public class HsUserManager:UserManager<BASE_USER,Guid>
11     {
12         /// <summary>
13         /// 通过构造函数注入用户存储实现类
14         /// </summary>
15         /// <param name="store"></param>
16         public HsUserManager(HsUserStore store) : base(store)
17         {
18 
19         }        
20     }
21 }

View Code

View Code

 

 

地点的代码非常特别轻巧,然则确很首要,你就知晓为将上边定义的HsUserStore注入到八个IOC容器里就好了。 即使不流入,你对顾客的有着读写db操作都尚未,后续一切报到、注册、验证专门的学问都力所不如执行!

上边包车型地铁代码特别极其简单,可是确很要紧,你就掌握为将方面定义的HsUserStore注入到三个IOC容器里就好了。 即使不流入,你对客商的有所读写db操作都并未有,后续一切报到、注册、验证专门的学业都心余力绌执行!

 

 

第六步承接SignInManager 类

第六步承继SignInManager 类

 SignInManager类是Microsoft.AspNet.Identity.Owin命名空间下的,集成了顾客登入举行管制的连锁API,是Asp.Net.Identity里不可或缺的管理类

 SignInManager类是Microsoft.AspNet.Identity.Owin命名空间下的,集成了客户登录进行保管的相关API,是Asp.Net.Identity里少不了的管理类

咱俩须求自定义三个签到处理类,承继SignInManager。

我们须求自定义三个登入管理类,承袭SignInManager。

澳门新萄京官方网站 105澳门新萄京官方网站 106

澳门新萄京官方网站 107澳门新萄京官方网站 108

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Web;
 5 using IdeintityDemo.Models;
 6 using Microsoft.AspNet.Identity;
 7 using Microsoft.AspNet.Identity.Owin;
 8 using Microsoft.Owin;
 9 using System.Threading.Tasks;
10 using Microsoft.Owin.Security;
11 
12 namespace IdeintityDemo.Identity
13 {
14     /// <summary>
15     /// 登录管理,此处用到了UserManager
16     /// </summary>
17     public class HsSignInManager : SignInManager<BASE_USER, Guid>
18     {
19 
20         /// <summary>
21         /// 构造函数
22         /// </summary>
23         /// <param name="UserManager"></param>
24         /// <param name="AuthenticationManager"></param>
25         public HsSignInManager(Microsoft.AspNet.Identity.UserManager<BASE_USER, Guid> UserManager, Microsoft.Owin.Security.IAuthenticationManager AuthenticationManager)
26             : base(UserManager, AuthenticationManager)
27         {
28 
29         }
30 
31         /// <summary>
32         /// 根据用户名密码,验证用户登录
33         /// </summary>
34         /// <param name="userName"></param>
35         /// <param name="password"></param>
36         /// <param name="isPersistent"></param>
37         /// <param name="shouldLockout"></param>
38         /// <returns></returns>
39         public override System.Threading.Tasks.Task<Microsoft.AspNet.Identity.Owin.SignInStatus> PasswordSignInAsync(string userName,
40                                                                                                                      string password,
41                                                                                                                      bool isPersistent,
42                                                                                                                      bool shouldLockout)
43         {
44             /*这里可以直接通过PasswordSignInAsync来校验,也可以重写~ */
45             //这里用Find方法会返回空的user。。。搞不懂。。
46             var user = base.UserManager.FindByName<BASE_USER, Guid>(userName);
47             if (user == null || user.Id == Guid.Empty)
48                 return Task.FromResult<SignInStatus>(SignInStatus.Failure);
49             else if (user.PWD != password)
50                 return Task.FromResult<SignInStatus>(SignInStatus.Failure);
51             else
52             {
53                 /*这个时候如果不写入到cooks里,在Home控制器的Index action里会被系统的
54                     Authorize刷选器拦截*/
55                 // 利用ASP.NET Identity获取identity 对象
56                 var identity = base.UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
57                 // 将上面拿到的identity对象登录
58                 base.AuthenticationManager.SignIn(new AuthenticationProperties()
59                 { IsPersistent = true }, identity.Result);
60                 return Task.FromResult<SignInStatus>(SignInStatus.Success);
61             }
62             /*这里如果你想直接使用微软的登入方法也可以,直接base.就ok啦*/
63             //return base.PasswordSignInAsync(userName,
64             //                                password,
65             //                                isPersistent,
66             //                                shouldLockout);
67         }
68 
69 
70     }
71 }
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Web;
 5 using IdeintityDemo.Models;
 6 using Microsoft.AspNet.Identity;
 7 using Microsoft.AspNet.Identity.Owin;
 8 using Microsoft.Owin;
 9 using System.Threading.Tasks;
10 using Microsoft.Owin.Security;
11 
12 namespace IdeintityDemo.Identity
13 {
14     /// <summary>
15     /// 登录管理,此处用到了UserManager
16     /// </summary>
17     public class HsSignInManager : SignInManager<BASE_USER, Guid>
18     {
19 
20         /// <summary>
21         /// 构造函数
22         /// </summary>
23         /// <param name="UserManager"></param>
24         /// <param name="AuthenticationManager"></param>
25         public HsSignInManager(Microsoft.AspNet.Identity.UserManager<BASE_USER, Guid> UserManager, Microsoft.Owin.Security.IAuthenticationManager AuthenticationManager)
26             : base(UserManager, AuthenticationManager)
27         {
28 
29         }
30 
31         /// <summary>
32         /// 根据用户名密码,验证用户登录
33         /// </summary>
34         /// <param name="userName"></param>
35         /// <param name="password"></param>
36         /// <param name="isPersistent"></param>
37         /// <param name="shouldLockout"></param>
38         /// <returns></returns>
39         public override System.Threading.Tasks.Task<Microsoft.AspNet.Identity.Owin.SignInStatus> PasswordSignInAsync(string userName,
40                                                                                                                      string password,
41                                                                                                                      bool isPersistent,
42                                                                                                                      bool shouldLockout)
43         {
44             /*这里可以直接通过PasswordSignInAsync来校验,也可以重写~ */
45             //这里用Find方法会返回空的user。。。搞不懂。。
46             var user = base.UserManager.FindByName<BASE_USER, Guid>(userName);
47             if (user == null || user.Id == Guid.Empty)
48                 return Task.FromResult<SignInStatus>(SignInStatus.Failure);
49             else if (user.PWD != password)
50                 return Task.FromResult<SignInStatus>(SignInStatus.Failure);
51             else
52             {
53                 /*这个时候如果不写入到cooks里,在Home控制器的Index action里会被系统的
54                     Authorize刷选器拦截*/
55                 // 利用ASP.NET Identity获取identity 对象
56                 var identity = base.UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
57                 // 将上面拿到的identity对象登录
58                 base.AuthenticationManager.SignIn(new AuthenticationProperties()
59                 { IsPersistent = true }, identity.Result);
60                 return Task.FromResult<SignInStatus>(SignInStatus.Success);
61             }
62             /*这里如果你想直接使用微软的登入方法也可以,直接base.就ok啦*/
63             //return base.PasswordSignInAsync(userName,
64             //                                password,
65             //                                isPersistent,
66             //                                shouldLockout);
67         }
68 
69 
70     }
71 }

View Code

View Code

 

 

下面最关键的措施就是PasswordSignInAsync,那些格局就是登陆方法。

地方最要害的方法便是PasswordSignInAsync,那些办法就是登录方法。

 

 

能够说大家封装的专门的学业早已实现了,封装了八个类

能够说我们封装的办事早就变成了,封装了多少个类

澳门新萄京官方网站 109

澳门新萄京官方网站 110

 

 

 未来我们看Controller里的代码是怎么写的呢~

 未来我们看Controller里的代码是怎么写的吗~

那是登记的Action,所属Controller当然是AccountController啦。。

那是登记的Action,所属Controller当然是AccountController啦。。

澳门新萄京官方网站 111澳门新萄京官方网站 112

澳门新萄京官方网站 113澳门新萄京官方网站 114

 1  /// <summary>
 2         /// 注册
 3         /// </summary>
 4         /// <returns></returns>
 5         [HttpPost]
 6         [AllowAnonymous]
 7         public async Task<ActionResult> Register(BASE_USER user)
 8         { 
 9             Microsoft.Owin.IOwinContext OwinContext = HttpContext.GetOwinContext();
10             //用户储存
11             HsUserStore userStore = new HsUserStore();
12             //UserManager
13             HsUserManager UserManager = new HsUserManager(userStore);
14             IdentityResult result = await UserManager.CreateAsync(user);
15             if (result.Succeeded)
16             {
17                 Response.Write("注册成功!");
18                 return RedirectToAction("index", "home");
19             }
20             return View();
21         }
22 
23         [AllowAnonymous]
24         public ActionResult Register()
25         {
26             return View();
27         }
 1  /// <summary>
 2         /// 注册
 3         /// </summary>
 4         /// <returns></returns>
 5         [HttpPost]
 6         [AllowAnonymous]
 7         public async Task<ActionResult> Register(BASE_USER user)
 8         { 
 9             Microsoft.Owin.IOwinContext OwinContext = HttpContext.GetOwinContext();
10             //用户储存
11             HsUserStore userStore = new HsUserStore();
12             //UserManager
13             HsUserManager UserManager = new HsUserManager(userStore);
14             IdentityResult result = await UserManager.CreateAsync(user);
15             if (result.Succeeded)
16             {
17                 Response.Write("注册成功!");
18                 return RedirectToAction("index", "home");
19             }
20             return View();
21         }
22 
23         [AllowAnonymous]
24         public ActionResult Register()
25         {
26             return View();
27         }

View Code

View Code

 

 

接下去是登陆的Action代码~

接下去是登入的Action代码~

澳门新萄京官方网站 115澳门新萄京官方网站 116

澳门新萄京官方网站 117澳门新萄京官方网站 118

 1         [AllowAnonymous]
 2         public ActionResult Login()
 3         {
 4             return View();
 5         }
 6 
 7 
 8         [HttpPost]
 9         [AllowAnonymous]
10         public async Task<ActionResult> Login(BASE_USER user)
11         {
12             if (string.IsNullOrEmpty(user.UserName)) { return View(); }
13             if (string.IsNullOrEmpty(user.PWD)) { return View(); }
14             //Context
15             Microsoft.Owin.IOwinContext OwinContext = HttpContext.GetOwinContext();
16             //实例化UserStore对象
17             HsUserStore userStore = new HsUserStore();
18             //UserManager
19             HsUserManager userManager = new HsUserManager(userStore);
20             //授权对象
21             IAuthenticationManager autherticationManager = OwinContext.Authentication;
22             //登录管理对象
23             HsSignInManager signManager = new HsSignInManager(userManager, autherticationManager);
24 
25             //登录
26             Microsoft.AspNet.Identity.Owin.SignInStatus SignInStatus = Microsoft.AspNet.Identity.Owin.SignInStatus.Failure;
27             try
28             {
29                 SignInStatus = await signManager.PasswordSignInAsync(user.UserName,
30                                                                      user.PWD,
31                                                                      true,
32                                                                      shouldLockout: false);
33              
34             }catch(Exception ea)
35             {
36                  
37             }
38             //登录状态
39             switch (SignInStatus)
40             {
41                 //成功 同一个Control里跳转直接使用RecirectToAction(ActionName) 
42                 case Microsoft.AspNet.Identity.Owin.SignInStatus.Success:                
43                     //不同控制器使用RedirectToAction
44                     return RedirectToAction("Index", "Home"); //可以直接跳到别的Controller.                
45                 //锁定
46                 case Microsoft.AspNet.Identity.Owin.SignInStatus.LockedOut:
47                     Response.Write("账户被锁定啦~~~!");
48                     break;
49                 //失败
50                 case Microsoft.AspNet.Identity.Owin.SignInStatus.Failure:
51                     Response.Write("登录失败啦~~~!");
52                     break;
53                 //要求验证
54                 case Microsoft.AspNet.Identity.Owin.SignInStatus.RequiresVerification:
55                     Response.Write("需要验证!");
56                     break;
57 
58             }
59             return View();
60         }
 1         [AllowAnonymous]
 2         public ActionResult Login()
 3         {
 4             return View();
 5         }
 6 
 7 
 8         [HttpPost]
 9         [AllowAnonymous]
10         public async Task<ActionResult> Login(BASE_USER user)
11         {
12             if (string.IsNullOrEmpty(user.UserName)) { return View(); }
13             if (string.IsNullOrEmpty(user.PWD)) { return View(); }
14             //Context
15             Microsoft.Owin.IOwinContext OwinContext = HttpContext.GetOwinContext();
16             //实例化UserStore对象
17             HsUserStore userStore = new HsUserStore();
18             //UserManager
19             HsUserManager userManager = new HsUserManager(userStore);
20             //授权对象
21             IAuthenticationManager autherticationManager = OwinContext.Authentication;
22             //登录管理对象
23             HsSignInManager signManager = new HsSignInManager(userManager, autherticationManager);
24 
25             //登录
26             Microsoft.AspNet.Identity.Owin.SignInStatus SignInStatus = Microsoft.AspNet.Identity.Owin.SignInStatus.Failure;
27             try
28             {
29                 SignInStatus = await signManager.PasswordSignInAsync(user.UserName,
30                                                                      user.PWD,
31                                                                      true,
32                                                                      shouldLockout: false);
33              
34             }catch(Exception ea)
35             {
36                  
37             }
38             //登录状态
39             switch (SignInStatus)
40             {
41                 //成功 同一个Control里跳转直接使用RecirectToAction(ActionName) 
42                 case Microsoft.AspNet.Identity.Owin.SignInStatus.Success:                
43                     //不同控制器使用RedirectToAction
44                     return RedirectToAction("Index", "Home"); //可以直接跳到别的Controller.                
45                 //锁定
46                 case Microsoft.AspNet.Identity.Owin.SignInStatus.LockedOut:
47                     Response.Write("账户被锁定啦~~~!");
48                     break;
49                 //失败
50                 case Microsoft.AspNet.Identity.Owin.SignInStatus.Failure:
51                     Response.Write("登录失败啦~~~!");
52                     break;
53                 //要求验证
54                 case Microsoft.AspNet.Identity.Owin.SignInStatus.RequiresVerification:
55                     Response.Write("需要验证!");
56                     break;
57 
58             }
59             return View();
60         }

View Code

View Code

 

 

 

 

我们看我们Mvc路由默许配置发轫页面是Home调节器下的Index

大家看我们Mvc路由暗中认可配置早先页面是Home调节器下的Index

澳门新萄京官方网站 119

澳门新萄京官方网站 120

 

 

 那是Home调控器下的Index申明

 那是Home调整器下的Index注解

澳门新萄京官方网站 121

澳门新萄京官方网站 122

 

 

 F5运维试下

 F5运作试下

 运维开掘浏览器间接跳转到了登入页面。。

 运营开采浏览器直接跳转到了登入页面。。

澳门新萄京官方网站 123

澳门新萄京官方网站 124

 

 

 

 

 

 

 在大家输入账号密码后,跳转到了Index页面

 在我们输入账号密码后,跳转到了Index页面

 

 

澳门新萄京官方网站 125

澳门新萄京官方网站 126

 

 

接下来我们用浏览器查看下cookie有哪些变化~

接下来大家用浏览器查看下cookie有怎么样变动~

澳门新萄京官方网站 127

澳门新萄京官方网站 128

 

 

发觉整整业务产生后浏览器保存了名称为_Identity德姆o的 多个积攒项,整个正是在大家登陆方法里施行的。

察觉一切事情成功后浏览器保存了名字为_Identity德姆o的 四个存款和储蓄项,整个便是在大家登陆方法里举行的。

 

 

好啊,整个Identity认证不依赖EF已经落实了,多少个大旨点就是内需贯彻IUser接口以及种种Store。。然后将落实各类Store的类注入到UserManager构造函数里

好啊,整个Identity认证不注重EF已经完毕了,多少个宗旨点正是索要完毕IUser接口以及各个Store。。然后将贯彻各类Store的类注入到UserManager构造函数里

等一时光再落到实处下Identity命名空间下的剧中人物管理这一块吧。。。不得不说微软的接口真是封装的好啊~

等有时光再落到实处下Identity命名空间下的剧中人物管理这一块呢。。。不得不说微软的接口真是封装的好哎~

此地多谢下开源中中原人民共和国的李朝强,笔者是看他博客再自个儿实施出来的~谢谢谢谢~

此间多谢下开源中夏族民共和国的李朝强,笔者是看他博客再本身试行出来的~多谢感激~

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

本文由澳门新萄京官方网站发布于www.8455.com,转载请注明出处:net中的线程同步基础,Framework达成格局

关键词: