微软关于CancellationTokenSource的介绍很简单,其实CancellationTokenSource的使用也很简单,但是实现就不是那么简单了,我们首先来看看CancellationTokenSource的实现:

public class CancellationTokenSource : IDisposable
{
private const int CANNOT_BE_CANCELED = ;
private const int NOT_CANCELED = ;
private const int NOTIFYING = ;
private const int NOTIFYINGCOMPLETE = ; private volatile int m_state;
private static readonly Action<object> s_LinkedTokenCancelDelegate = new Action<object>(LinkedTokenCancelDelegate);
private static readonly int s_nLists = (PlatformHelper.ProcessorCount > ) ? : PlatformHelper.ProcessorCount;
private volatile CancellationCallbackInfo m_executingCallback;
private volatile SparselyPopulatedArray<CancellationCallbackInfo>[] m_registeredCallbacksLists;
private static readonly TimerCallback s_timerCallback = new TimerCallback(TimerCallbackLogic);
private volatile Timer m_timer; public CancellationTokenSource()
{
m_state = NOT_CANCELED;
} //Constructs a CancellationTokenSource that will be canceled after a specified time span.
public CancellationTokenSource(Int32 millisecondsDelay)
{
if (millisecondsDelay < -)
{
throw new ArgumentOutOfRangeException("millisecondsDelay");
} InitializeWithTimer(millisecondsDelay);
} private void InitializeWithTimer(Int32 millisecondsDelay)
{
m_state = NOT_CANCELED;
m_timer = new Timer(s_timerCallback, this, millisecondsDelay, -1);
} private static void TimerCallbackLogic(object obj)
{
CancellationTokenSource cts = (CancellationTokenSource)obj;
if (!cts.IsDisposed)
{
try
{
cts.Cancel(); // will take care of disposing of m_timer
}
catch (ObjectDisposedException)
{
if (!cts.IsDisposed) throw;
}
}
} public void Cancel()
{
Cancel(false);
} public void Cancel(bool throwOnFirstException)
{
ThrowIfDisposed();
NotifyCancellation(throwOnFirstException);
} public void CancelAfter(Int32 millisecondsDelay)
{
ThrowIfDisposed(); if (millisecondsDelay < -)
{
throw new ArgumentOutOfRangeException("millisecondsDelay");
} if (IsCancellationRequested) return;
if (m_timer == null)
{
Timer newTimer = new Timer(s_timerCallback, this, -1, -1);
if (Interlocked.CompareExchange(ref m_timer, newTimer, null) != null)
{
newTimer.Dispose();
}
} // It is possible that m_timer has already been disposed, so we must do
// the following in a try/catch block.
try
{
m_timer.Change(millisecondsDelay, -1);
}
catch (ObjectDisposedException)
{
}
} private void NotifyCancellation(bool throwOnFirstException)
{
if (IsCancellationRequested)
return; // If we're the first to signal cancellation, do the main extra work.
if (Interlocked.CompareExchange(ref m_state, NOTIFYING, NOT_CANCELED) == NOT_CANCELED)
{
Timer timer = m_timer;
if(timer != null) timer.Dispose(); //record the threadID being used for running the callbacks.
ThreadIDExecutingCallbacks = Thread.CurrentThread.ManagedThreadId; //If the kernel event is null at this point, it will be set during lazy construction.
if (m_kernelEvent != null)
m_kernelEvent.Set(); // update the MRE value. ExecuteCallbackHandlers(throwOnFirstException);
Contract.Assert(IsCancellationCompleted, "Expected cancellation to have finished");
}
} /// Invoke the Canceled event. The handlers are invoked synchronously in LIFO order.
private void ExecuteCallbackHandlers(bool throwOnFirstException)
{
Contract.Assert(IsCancellationRequested, "ExecuteCallbackHandlers should only be called after setting IsCancellationRequested->true");
Contract.Assert(ThreadIDExecutingCallbacks != -, "ThreadIDExecutingCallbacks should have been set."); List<Exception> exceptionList = null;
SparselyPopulatedArray<CancellationCallbackInfo>[] callbackLists = m_registeredCallbacksLists; if (callbackLists == null)
{
Interlocked.Exchange(ref m_state, NOTIFYINGCOMPLETE);
return;
} try
{
for (int index = ; index < callbackLists.Length; index++)
{
SparselyPopulatedArray<CancellationCallbackInfo> list = Volatile.Read<SparselyPopulatedArray<CancellationCallbackInfo>>(ref callbackLists[index]);
if (list != null)
{
SparselyPopulatedArrayFragment<CancellationCallbackInfo> currArrayFragment = list.Tail; while (currArrayFragment != null)
{
for (int i = currArrayFragment.Length - ; i >= ; i--)
{
m_executingCallback = currArrayFragment[i];
if (m_executingCallback != null)
{
CancellationCallbackCoreWorkArguments args = new CancellationCallbackCoreWorkArguments(currArrayFragment, i);
try
{
if (m_executingCallback.TargetSyncContext != null)
{
m_executingCallback.TargetSyncContext.Send(CancellationCallbackCoreWork_OnSyncContext, args);
ThreadIDExecutingCallbacks = Thread.CurrentThread.ManagedThreadId;
}
else
{
CancellationCallbackCoreWork(args);
}
}
catch(Exception ex)
{
if (throwOnFirstException)
throw;
if(exceptionList == null)
exceptionList = new List<Exception>();
exceptionList.Add(ex);
}
}
}
currArrayFragment = currArrayFragment.Prev;
}
}
}
}
finally
{
m_state = NOTIFYINGCOMPLETE;
m_executingCallback = null;
Thread.MemoryBarrier(); // for safety, prevent reorderings crossing this point and seeing inconsistent state.
} if (exceptionList != null)
{
Contract.Assert(exceptionList.Count > , "Expected exception count > 0");
throw new AggregateException(exceptionList);
}
} private void CancellationCallbackCoreWork_OnSyncContext(object obj)
{
CancellationCallbackCoreWork((CancellationCallbackCoreWorkArguments)obj);
} private void CancellationCallbackCoreWork(CancellationCallbackCoreWorkArguments args)
{
CancellationCallbackInfo callback = args.m_currArrayFragment.SafeAtomicRemove(args.m_currArrayIndex, m_executingCallback);
if (callback == m_executingCallback)
{
if (callback.TargetExecutionContext != null)
{
callback.CancellationTokenSource.ThreadIDExecutingCallbacks = Thread.CurrentThread.ManagedThreadId;
}
callback.ExecuteCallback();
}
} public static CancellationTokenSource CreateLinkedTokenSource(CancellationToken token1, CancellationToken token2)
{
CancellationTokenSource linkedTokenSource = new CancellationTokenSource();
bool token2CanBeCanceled = token2.CanBeCanceled; if( token1.CanBeCanceled )
{
linkedTokenSource.m_linkingRegistrations = new CancellationTokenRegistration[token2CanBeCanceled ? : ]; // there will be at least 1 and at most 2 linkings
linkedTokenSource.m_linkingRegistrations[] = token1.InternalRegisterWithoutEC(s_LinkedTokenCancelDelegate, linkedTokenSource);
} if( token2CanBeCanceled )
{
int index = ;
if( linkedTokenSource.m_linkingRegistrations == null )
{
linkedTokenSource.m_linkingRegistrations = new CancellationTokenRegistration[]; // this will be the only linking
index = ;
}
linkedTokenSource.m_linkingRegistrations[index] = token2.InternalRegisterWithoutEC(s_LinkedTokenCancelDelegate, linkedTokenSource);
}
return linkedTokenSource;
} public static CancellationTokenSource CreateLinkedTokenSource(params CancellationToken[] tokens)
{
if (tokens == null)
throw new ArgumentNullException("tokens"); if (tokens.Length == )
throw new ArgumentException(Environment.GetResourceString("CancellationToken_CreateLinkedToken_TokensIsEmpty")); Contract.EndContractBlock(); CancellationTokenSource linkedTokenSource = new CancellationTokenSource();
linkedTokenSource.m_linkingRegistrations = new CancellationTokenRegistration[tokens.Length]; for (int i = ; i < tokens.Length; i++)
{
if (tokens[i].CanBeCanceled)
{
linkedTokenSource.m_linkingRegistrations[i] = tokens[i].InternalRegisterWithoutEC(s_LinkedTokenCancelDelegate, linkedTokenSource);
}
}
return linkedTokenSource;
} internal CancellationTokenRegistration InternalRegister(Action<object> callback, object stateForCallback, SynchronizationContext targetSyncContext, ExecutionContext executionContext)
{
if (AppContextSwitches.ThrowExceptionIfDisposedCancellationTokenSource)
{
ThrowIfDisposed();
}
Contract.Assert(CanBeCanceled, "Cannot register for uncancelable token src");
if (!IsCancellationRequested)
{
if (m_disposed && !AppContextSwitches.ThrowExceptionIfDisposedCancellationTokenSource)
return new CancellationTokenRegistration(); int myIndex = Thread.CurrentThread.ManagedThreadId % s_nLists; CancellationCallbackInfo callbackInfo = new CancellationCallbackInfo(callback, stateForCallback, targetSyncContext, executionContext, this); //allocate the callback list array
var registeredCallbacksLists = m_registeredCallbacksLists;
if (registeredCallbacksLists == null)
{
SparselyPopulatedArray<CancellationCallbackInfo>[] list = new SparselyPopulatedArray<CancellationCallbackInfo>[s_nLists];
registeredCallbacksLists = Interlocked.CompareExchange(ref m_registeredCallbacksLists, list, null);
if (registeredCallbacksLists == null) registeredCallbacksLists = list;
} //allocate the actual lists on-demand to save mem in low-use situations, and to avoid false-sharing.
var callbacks = Volatile.Read<SparselyPopulatedArray<CancellationCallbackInfo>>(ref registeredCallbacksLists[myIndex]);
if (callbacks == null)
{
SparselyPopulatedArray<CancellationCallbackInfo> callBackArray = new SparselyPopulatedArray<CancellationCallbackInfo>();
Interlocked.CompareExchange(ref (registeredCallbacksLists[myIndex]), callBackArray, null);
callbacks = registeredCallbacksLists[myIndex];
} // Now add the registration to the list.
SparselyPopulatedArrayAddInfo<CancellationCallbackInfo> addInfo = callbacks.Add(callbackInfo);
CancellationTokenRegistration registration = new CancellationTokenRegistration(callbackInfo, addInfo); if (!IsCancellationRequested)
return registration; bool deregisterOccurred = registration.TryDeregister(); if (!deregisterOccurred)
{
return registration;
}
}
// If cancellation already occurred, we run the callback on this thread and return an empty registration.
callback(stateForCallback);
return new CancellationTokenRegistration();
} public bool IsCancellationRequested
{
get { return m_state >= NOTIFYING; }
} internal bool IsCancellationCompleted
{
get { return m_state == NOTIFYINGCOMPLETE; }
} public CancellationToken Token
{
get
{
ThrowIfDisposed();
return new CancellationToken(this);
}
}
internal CancellationCallbackInfo ExecutingCallback
{
get { return m_executingCallback; }
} private static void LinkedTokenCancelDelegate(object source)
{
CancellationTokenSource cts = source as CancellationTokenSource;
Contract.Assert(source != null);
cts.Cancel();
}
}

CancellationTokenSource的实现相对比较复杂,我们首先看看CancellationTokenSource的构造函数,默认构造函数将会设置【m_state = NOT_CANCELED】,我们也可以构造一个特定时间后就自动Cancel的CancellationTokenSource,自动Cancel是依赖一个Timer实例,在Timer到指定时间后调用CancellationTokenSource的Cancel方法【这里是在TimerCallbackLogic里面调用Cancel方法】,CancelAfter方法的实现也是依赖这个Timer实例和TimerCallbackLogic方法。

现在我们来看看CancellationTokenSource最主要的一个方法Cancel,Cancel方法调用NotifyCancellation方法,NotifyCancellation方法主要调用ExecuteCallbackHandlers【从这个方法的名称可以猜测到主要是调用回调方法】,在ExecuteCallbackHandlers方法里面用到一个变量m_registeredCallbacksLists,它是SparselyPopulatedArray<CancellationCallbackInfo>[]结构,【可以理解为是一个链表的数组,数组每个元素时一个链表,链表里面的每个节点都可以访问下一个节点】,我们遍历这个链表数组的每一个节点,检查节点是否有值,即m_executingCallback != null,然后调用回调方法,如果回调方法的TargetSyncContext不为空,调用CancellationCallbackCoreWork_OnSyncContext方法,否者调用CancellationCallbackCoreWork方法【CancellationCallbackCoreWork_OnSyncContext里面也是调用它】,CancellationCallbackCoreWork方法是调用CancellationCallbackInfo的ExecuteCallback。

CancellationTokenSource有两个CreateLinkedTokenSource方法【可以理解为创建于当前的CreateLinkedTokenSource相关联的CreateLinkedTokenSource】,期主要实现是CancellationToken的Register方法。

public struct CancellationToken
{
private CancellationTokenSource m_source;
internal CancellationToken(CancellationTokenSource source)
{
m_source = source;
}
public CancellationToken(bool canceled) :this()
{
if(canceled)
m_source = CancellationTokenSource.InternalGetStaticSource(canceled);
} public CancellationTokenRegistration Register(Action callback)
{
if (callback == null)
throw new ArgumentNullException("callback"); return Register(s_ActionToActionObjShunt,callback,false,true);
} public CancellationTokenRegistration Register(Action callback, bool useSynchronizationContext)
{
if (callback == null)
throw new ArgumentNullException("callback"); return Register(s_ActionToActionObjShunt,callback,useSynchronizationContext,true);
} public CancellationTokenRegistration Register(Action<Object> callback, Object state)
{
if (callback == null)
throw new ArgumentNullException("callback"); return Register(callback,state,false,true);
} /// Registers a delegate that will be called when this CancellationToken is canceled.
public CancellationTokenRegistration Register(Action<Object> callback, Object state, bool useSynchronizationContext)
{
return Register(callback,state,useSynchronizationContext,true);
} private CancellationTokenRegistration Register(Action<Object> callback, Object state, bool useSynchronizationContext, bool useExecutionContext)
{
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; if (callback == null)
throw new ArgumentNullException("callback"); if (CanBeCanceled == false)
{
return new CancellationTokenRegistration(); // nothing to do for tokens than can never reach the canceled state. Give them a dummy registration.
} SynchronizationContext capturedSyncContext = null;
ExecutionContext capturedExecutionContext = null;
if (!IsCancellationRequested)
{
if (useSynchronizationContext)
capturedSyncContext = SynchronizationContext.Current;
if (useExecutionContext)
capturedExecutionContext = ExecutionContext.Capture(ref stackMark, ExecutionContext.CaptureOptions.OptimizeDefaultCase);
} // Register the callback with the source.
return m_source.InternalRegister(callback, state, capturedSyncContext, capturedExecutionContext);
} private readonly static Action<Object> s_ActionToActionObjShunt = new Action<Object>(ActionToActionObjShunt);
private static void ActionToActionObjShunt(object obj)
{
Action action = obj as Action;
Contract.Assert(action != null, "Expected an Action here");
action();
} public static CancellationToken None
{
get { return default(CancellationToken); }
}
public bool IsCancellationRequested
{
get
{
return m_source != null && m_source.IsCancellationRequested;
}
} public bool CanBeCanceled
{
get
{
return m_source != null && m_source.CanBeCanceled;
}
}
public void ThrowIfCancellationRequested()
{
if (IsCancellationRequested)
ThrowOperationCanceledException();
}
private void ThrowOperationCanceledException()
{
throw new OperationCanceledException(Environment.GetResourceString("OperationCanceled"), this);
}
}

CancellationToken的很多属性都是来源于CancellationTokenSource的属性,CancellationToken的主要方法 Register 也是嗲用CancellationTokenSource的InternalRegister方法。InternalRegister方法检查当前是否发起了Cancel【IsCancellationRequested】,如果是直接调用回调方法callback(stateForCallback);,否者把回调方法包装成CancellationCallbackInfo实例,然后添加到m_registeredCallbacksLists对象中,然后在返回CancellationTokenRegistration实例。

    internal class CancellationCallbackInfo
{
internal readonly Action<object> Callback;
internal readonly object StateForCallback;
internal readonly SynchronizationContext TargetSyncContext;
internal readonly ExecutionContext TargetExecutionContext;
internal readonly CancellationTokenSource CancellationTokenSource; internal CancellationCallbackInfo(Action<object> callback, object stateForCallback, SynchronizationContext targetSyncContext, ExecutionContext targetExecutionContext,CancellationTokenSource cancellationTokenSource)
{
Callback = callback;
StateForCallback = stateForCallback;
TargetSyncContext = targetSyncContext;
TargetExecutionContext = targetExecutionContext;
CancellationTokenSource = cancellationTokenSource;
} private static ContextCallback s_executionContextCallback;
internal void ExecuteCallback()
{
if (TargetExecutionContext != null)
{
var callback = s_executionContextCallback;
if (callback == null) s_executionContextCallback = callback = new ContextCallback(ExecutionContextCallback); ExecutionContext.Run(TargetExecutionContext, callback, this);
}
else
{
ExecutionContextCallback(this);
}
} private static void ExecutionContextCallback(object obj)
{
CancellationCallbackInfo callbackInfo = obj as CancellationCallbackInfo;
Contract.Assert(callbackInfo != null);
callbackInfo.Callback(callbackInfo.StateForCallback);
}
} internal class SparselyPopulatedArray<T> where T : class
{
private readonly SparselyPopulatedArrayFragment<T> m_head;
private volatile SparselyPopulatedArrayFragment<T> m_tail;
internal SparselyPopulatedArray(int initialSize)
{
m_head = m_tail = new SparselyPopulatedArrayFragment<T>(initialSize);
} internal SparselyPopulatedArrayFragment<T> Tail
{
get { return m_tail; }
} internal SparselyPopulatedArrayAddInfo<T> Add(T element)
{
while (true)
{
// Get the tail, and ensure it's up to date.
SparselyPopulatedArrayFragment<T> tail = m_tail;
while (tail.m_next != null)
m_tail = (tail = tail.m_next); // Search for a free index, starting from the tail.
SparselyPopulatedArrayFragment<T> curr = tail;
while (curr != null)
{
const int RE_SEARCH_THRESHOLD = -; // Every 10 skips, force a search.
if (curr.m_freeCount < )
--curr.m_freeCount; if (curr.m_freeCount > || curr.m_freeCount < RE_SEARCH_THRESHOLD)
{
int c = curr.Length;
int start = ((c - curr.m_freeCount) % c);
if (start < )
{
start = ;
curr.m_freeCount--; // Too many free elements; fix up.
}
Contract.Assert(start >= && start < c, "start is outside of bounds"); // Now walk the array until we find a free slot (or reach the end).
for (int i = ; i < c; i++)
{
// If the slot is null, try to CAS our element into it.
int tryIndex = (start + i) % c;
Contract.Assert(tryIndex >= && tryIndex < curr.m_elements.Length, "tryIndex is outside of bounds"); if (curr.m_elements[tryIndex] == null && Interlocked.CompareExchange(ref curr.m_elements[tryIndex], element, null) == null)
{
int newFreeCount = curr.m_freeCount - ;
curr.m_freeCount = newFreeCount > ? newFreeCount : ;
return new SparselyPopulatedArrayAddInfo<T>(curr, tryIndex);
}
}
} curr = curr.m_prev;
} // If we got here, we need to add a new chunk to the tail and try again.
SparselyPopulatedArrayFragment<T> newTail = new SparselyPopulatedArrayFragment<T>(
tail.m_elements.Length == ? : tail.m_elements.Length * , tail);
if (Interlocked.CompareExchange(ref tail.m_next, newTail, null) == null)
{
m_tail = newTail;
}
}
}
} internal struct SparselyPopulatedArrayAddInfo<T> where T : class
{
private SparselyPopulatedArrayFragment<T> m_source;
private int m_index; internal SparselyPopulatedArrayAddInfo(SparselyPopulatedArrayFragment<T> source, int index)
{
Contract.Assert(source != null);
Contract.Assert(index >= && index < source.Length);
m_source = source;
m_index = index;
} internal SparselyPopulatedArrayFragment<T> Source
{
get { return m_source; }
} internal int Index
{
get { return m_index; }
}
} internal class SparselyPopulatedArrayFragment<T> where T : class
{
internal readonly T[] m_elements; // The contents, sparsely populated (with nulls).
internal volatile int m_freeCount; // A hint of the number of free elements.
internal volatile SparselyPopulatedArrayFragment<T> m_next; // The next fragment in the chain.
internal volatile SparselyPopulatedArrayFragment<T> m_prev; // The previous fragment in the chain. internal SparselyPopulatedArrayFragment(int size) : this(size, null)
{
} internal SparselyPopulatedArrayFragment(int size, SparselyPopulatedArrayFragment<T> prev)
{
m_elements = new T[size];
m_freeCount = size;
m_prev = prev;
} internal T this[int index]
{
get { return Volatile.Read<T>(ref m_elements[index]); }
} internal int Length
{
get { return m_elements.Length; }
} internal SparselyPopulatedArrayFragment<T> Prev
{
get { return m_prev; }
} internal T SafeAtomicRemove(int index, T expectedElement)
{
T prevailingValue = Interlocked.CompareExchange(ref m_elements[index], null, expectedElement);
if (prevailingValue != null)
++m_freeCount;
return prevailingValue;
}
}

回头看CancellationCallbackInfo的实现也很简单。

C# CancellationTokenSource和CancellationToken的实现的更多相关文章

  1. CancellationTokenSource 和 CancellationToken 取消线程

    Main 程序[分别调用三个方法] static void Main(string[] args) { using (CancellationTokenSource cts = new Cancell ...

  2. C#中CancellationToken和CancellationTokenSource用法

    之前做开发时,一直没注意这个东西,做了.net core之后,发现CancellationToken用的越来越平凡了. 这也难怪,原来.net framework使用异步的不是很多,而.net cor ...

  3. 在C#中使用 CancellationToken 处理异步任务

    在 .NET Core 中使用异步编程已经很普遍了, 你在项目中随处可见 async 和 await,它简化了异步操作,允许开发人员,使用同步的方式编写异步代码,你会发现在大部分的异步方法中,都提供了 ...

  4. 在ASP.NET Core中用HttpClient(五)——通过CancellationToken取消HTTP请求

    ​用户向服务器发送HTTP请求应用程序页面是一种非常可能的情况.当我们的应用程序处理请求时,用户可以从该页面离开.在这种情况下,我们希望取消HTTP请求,因为响应对该用户不再重要.当然,这只是实际应用 ...

  5. 浅谈C#取消令牌CancellationTokenSource

    前言 相信大家在使用C#进行开发的时候,特别是使用异步的场景,多多少少会接触到CancellationTokenSource.看名字就知道它和取消异步任务相关的,而且一看便知大名鼎鼎的Cancella ...

  6. C#CancellationToken/CancellationTokenSource-取消令牌/取消令牌源 CT/CTS

    详细情况:https://www.cnblogs.com/wucy/p/15128365.html 背景 为什么引入取消令牌? Thread.abort()方法会破坏同步锁中代码的原子逻辑,破坏锁的作 ...

  7. Asp.Net WebApi核心对象解析(下篇)

    在接着写Asp.Net WebApi核心对象解析(下篇)之前,还是一如既往的扯扯淡,元旦刚过,整个人还是处于晕的状态,一大早就来处理系统BUG,简直是坑爹(好在没让我元旦赶过来该BUG),队友挖的坑, ...

  8. .Net多线程编程—System.Threading.Tasks.Parallel

    System.Threading.Tasks.Parallel类提供了Parallel.Invoke,Parallel.For,Parallel.ForEach这三个静态方法. 1 Parallel. ...

  9. .Net多线程编程—任务Task

    1 System.Threading.Tasks.Task简介 一个Task表示一个异步操作,Task的创建和执行是独立的. 只读属性: 返回值 名称 说明 object AsyncState 表示在 ...

随机推荐

  1. 【转】Linux 虚拟内存和物理内存的理解

    http://www.cnblogs.com/dyllove98/archive/2013/06/12/3132940.html 首先,让我们看下虚拟内存: 第一层理解 1.         每个进程 ...

  2. 第一章:python基础语法| 字符编码| 条件语句...

    1.编程语言介绍 编程就是写代码,让计算机帮你做事情.计算机底层是电路,只认识二进制0和1.机器语言&汇编语言语言进化历史:机器.汇编.高级.机器语言只接受二进制代码:汇编语言是采用英文缩写的 ...

  3. 组合数的简单求法(dfs)

    组合数的具体应用可以参考这个例子:https://www.cnblogs.com/FengZeng666/p/10496223.html 下面这段代码可以作为求组合数的固定套路或者公式: #inclu ...

  4. CentOS7配置FTP服务器增强版~(零基础学会FTP配置)

    ps:原文不知出处,但是原文也不能正常启动,这里做了一些修改!如果能正常配置请在下方留言让更多的人看到,因为之前我本人照着网上的教程安装卸载了十多次也无法正常使用,不希望后面的兄弟继续浪费时间,如果不 ...

  5. JavaScript深拷贝实现原理简析

    原文:http://www.cnblogs.com/xie-zhan/p/6020954.html JavaScript实现继承的时候,需要进行对象的拷贝:而为了不影响拷贝后的数据对原数据造成影响,也 ...

  6. Dataset:利用Python将已有mnist数据集通过移动像素上下左右的方法来扩大数据集为初始数据集的5倍—Jason niu

    from __future__ import print_function import cPickle import gzip import os.path import random import ...

  7. jOOR

    --摘自<android插件化开发指南> 1.jOOR库就一个Reflect.java类很重要 2.Reflect.java包括6个核心方法 1)on:包裹一个类或者对象,表示在这个类或对 ...

  8. 20172328《程序设计与数据结构》实验三 敏捷开发与XP实践报告

    20172328<程序设计与数据结构>实验三 敏捷开发与XP实践报告 课程:<程序设计与数据结构> 班级: 1723 姓名: 李馨雨 学号:20172328 实验教师:王志强 ...

  9. luogu P2962 [USACO09NOV]灯Lights 高斯消元

    目录 题目链接 题解 题目链接 luogu P2962 [USACO09NOV]灯Lights 题解 可以折半搜索 map合并 复杂度 2^(n / 2)*logn 高斯消元后得到每个点的翻转状态 爆 ...

  10. Scrapy基础(五) ------css选择器基础

    基本语法: *                  选择所有节点#container         选择id为container的节点.container      选择所有class包含contai ...