将 .NET 任务作为 WinRT 异步操作公开
转自:http://blogs.msdn.com/b/windowsappdev_cn/archive/2012/06/22/net-winrt.aspx
在博文深入探究 Await 和 WinRT 中,我们讨论了 C# 和 Visual Basic 中新增的 async 和 await 关键字,以及如何使用它们执行 Windows 运行时 (WinRT) 异步操作。
在 .NET 基本类库 (BCL) 的辅助下,您还可以使用这些关键字来设计异步操作,然后通过 WinRT 公开这些操作,以使其适用于使用其他语言构建的组件。在本篇博文中,我们将探究如何实现。(有关使用 C# 或 Visual Basic 实施 WinRT 组件的完整详细信息,请参阅使用 C# 和 Visual Basic 创建 Windows 运行时组件。)
首先,我们来回顾一下 WinRT 中异步 API 的概况。
WinRT 异步接口
WinRT 具有多个与异步操作相关的接口。第一个要说的就是 IAsyncInfo,每个有效的 WinRT 异步操作都会实施该接口。它可提供异步操作的所有常用功能,包括操作的当前状态、操作失败时的错误信息、操作 ID 以及操作请求取消的能力:
public interface IAsyncInfo
{
    AsyncStatus Status { get; }
    Exception ErrorCode { get; }
    uint Id { get; }
    void Cancel();
    void Close();
}除实施 IAsyncInfo 之外,所有有效的 WinRT 异步操作还需要实施另外四个接口之一:IAsyncAction、IAsyncActionWithProgress、IAsyncOperation 或 IAsyncOperationWithProgress
// No results, no progress
public interface IAsyncAction : IAsyncInfo
{
    AsyncActionCompletedHandler Completed { get; set; }
    void GetResults();
}
// No results, with progress
public interface IAsyncActionWithProgress<TProgress> : IAsyncInfo
{
    AsyncActionWithProgressCompletedHandler<TProgress> Completed { get; set; }
    AsyncActionProgressHandler<TProgress> Progress { get; set; }
    void GetResults();
}
// With results, no progress
public interface IAsyncOperation<TResult> : IAsyncInfo
{
    AsyncOperationCompletedHandler<TResult> Completed { get; set; }
    TResult GetResults();
}
// With results, with progress
public interface IAsyncOperationWithProgress<TResult,TProgress> : IAsyncInfo
{
    AsyncOperationWithProgressCompletedHandler<TResult,TProgress> Completed { get; set; }
    AsyncOperationProgressHandler<TResult,TProgress> Progress { get; set; }
    TResult GetResults();
}(IAsyncAction 提供 GetResults 方法,即使没有返回结果也是如此。这就为失败的异步操作提供了一种抛出异常的方法,而不必迫使所有用户访问 IAsyncInfo 的 ErrorCode 属性。这与 C# 和 Visual Basic 中的 awaiter 类型公开 GetResult 方法相类似,尽管 GetResult 方法被类型化为返回 void。)
在构建 WinRT 库时,该库中所有全局公开的异步操作都会强类型化为返回这四个接口之一。与此相对,从 .NET 库公开的新异步操作遵循基于任务的异步模式 (TAP),对于不返回结果的操作返回 Task,而对于返回结果的操作则返回 Task。
Task 和 Task 不会实施这些 WinRT 接口,公共语言运行时 (CLR) 也不会暗中掩饰它们的差异(对于某些类型会如此,例如 WinRT Windows.Foundation.Uri 类型和 BCL System.Uri 类型)。而是我们需要明确地从一种模式转换到另一种。在博文深入探究 Await 和 WinRT 中,我们了解了 BCL 中的 AsTask 扩展方法如何提供一种明确的可重用机制来从 WinRT 异步接口转换为 .NET 任务。BCL 还支持反向转换,即通过某些方法从 .NET 任务转换为 WinRT 异步接口。
使用 AsAsyncAction 和 AsAsyncOperation 转换
在本篇博文中,我们假设有一个 .NET 异步方法 DownloadStringAsyncInternal。我们向其传递一个指向某网页的 URL,该方法会异步下载并以字符串的形式返回该网页的内容:
internal static Task<string> DownloadStringAsyncInternal(string url);如何实施该方法并不重要。我们的目标是将该方法封装成一个 WinRT 异步操作,也就是说作为一种可以返回上述四个接口之一的方法。由于我们的操作会产生一个结果(一个字符串),并且它不支持进度报告,因此我们的 WinRT 异步操作会返回 IAsyncOperation:
public static IAsyncOperation<string> DownloadStringAsync(string url);为实施此方法,我们可以调用 DownloadStringAsyncInternal 方法来获取结果 Task。接下来我们需要将该任务转换成所需的 IAsyncOperation…但如何实现?
public static IAsyncOperation<string> DownloadStringAsync(string url)
{
    Task<string> from = DownloadStringAsyncInternal(url);
    IAsyncOperation<string> to = ...; // TODO: how do we convert 'from'?
    return to;
}为填补这一空当,.NET 4.5 中的 System.Runtime.WindowsRuntime.dll 程序集包含了用于 Task 和 Task 的扩展方法,可提供所需的转换:
// in System.Runtime.WindowsRuntime.dll
public static class WindowsRuntimeSystemExtensions
{
    public static IAsyncAction AsAsyncAction(
        this Task source);
    public static IAsyncOperation<TResult> AsAsyncOperation<TResult>(
        this Task<TResult> source);
    ...
}这些方法会返回一个新的 IAsyncAction 或 IAsyncOperation 实例,该实例会分别封装所提供的 Task 或 Task(由于 Task 派生自 Task,因此这两种方法都可以用于 Task,但将 AsAsyncAction 与返回结果的异步方法一同使用是相对少见的情况)。从逻辑上讲,您可以将这些操作视为明确的可重复的异步操作,或者从设计模式的角度出发,视为适配器。它们会返回一个代表底层任务但公开 WinRT 所需的外围应用的实例。使用此类扩展方法,我们可以完成 DownloadStringAsync 实施:
public static IAsyncOperation<string> DownloadStringAsync(string url)
{
    Task<string> from = DownloadStringAsyncInternal(url);
    IAsyncOperation<string> to = from.AsAsyncOperation();
    return to;
}我们还可以以更简洁的方式进行编写,重点强调操作的可重用性:
public static IAsyncOperation<string> DownloadStringAsync(string url)
{
    return DownloadStringAsyncInternal(url).AsAsyncOperation();
}DownloadStringAsyncInternal 会在我们调用 AsAsyncOperation 之前进行调用。这意味着我们需要异步调用 DownloadStringAsyncInternal 来快速返回,以确保 DownloadStringAsync 封装程序方法有响应。如果出于某些原因,您担心异步操作花费时间过长,或者出于其他原因您明确希望将调用转移到某个线程池,则可以通过使用 Task.Run,然后在其返回的任务中调用 AsAsyncOperation 来实现:
public static IAsyncOperation<string> DownloadStringAsync(string url)
{
    return Task.Run(()=>DownloadStringAsyncInternal(url)).AsAsyncOperation();
}AsyncInfo.Run 提供更多灵活性
这些内置的 AsAsyncAction 和 AsAsyncOperation 扩展方法对于从 Task 到 IAsyncAction 和从 Task 到 IAsyncOperation 的简单转换而言十分有用。但对于更高级的转换而言效果如何?
System.Runtime.WindowsRuntime.dll 所包含的另一个类型可以提供更多灵活性:AsyncInfo,包含在 System.Runtime.InteropServices.WindowsRuntime 命名空间中。AsyncInfo 公开静态 Run 方法的四个重载,每个分别对应四个 WinRT 异步接口之一:
// in System.Runtime.WindowsRuntime.dll
public static class AsyncInfo
{
    // No results, no progress
    public static IAsyncAction Run(
        Func<CancellationToken,
             Task> taskProvider); 
    // No results, with progress
    public static IAsyncActionWithProgress<TProgress> Run<TProgress>(
        Func<CancellationToken,
             IProgress<TProgress>,
             Task> taskProvider);
    // With results, no progress
    public static IAsyncOperation<TResult> Run<TResult>(
        Func<CancellationToken,
             Task<TResult>> taskProvider);
    // With results, with progress
    public static IAsyncOperationWithProgress<TResult, TProgress> Run<TResult, TProgress>(
        Func<CancellationToken,
             IProgress<TProgress>,
             Task<TResult>> taskProvider);
}我们已检验过的 AsAsyncAction 和 AsAsyncOperation 方法接受 Task 作为参数。与此相对,这些 Run 方法接受返回 Task 的函数委派,而 Task 和 Func<…,Task> 之间的差别已足够为我们提供更高级操作所需的额外灵活性。
从逻辑上讲,您可以将 AsAsyncAction 和 AsAsyncOperation 视为对更高级的 AsyncInfo.Run 的简单辅助:
public static IAsyncAction AsAsyncAction(
    this Task source)
{
    return AsyncInfo.Run(_ => source);
}
public static IAsyncOperation<TResult> AsAsyncOperation<TResult>(
    this Task<TResult> source)
{
    return AsyncInfo.Run(_ => source);
}这并不是它们在 .NET 4.5 中的确切实施方法,但所发挥的功能就是这样,因此,可以将它们理解为基本支持和高级支持的差别。在简单情况下可以使用 AsAsyncAction 和 AsAsyncOperation,但在更高级的情况下,AsyncInfo.Run 则会大放异彩。
取消
AsyncInfo.Run 可实现通过 WinRT 异步方法支持取消。
继续我们的下载示例,假设我们有另一个接受 CancellationToken 的 DownloadStringAsyncInternal 重载:
internal static Task<string> DownloadStringAsyncInternal(
    string url, CancellationToken cancellationToken);CancellationToken 是一种 .NET Framework 类型,以可组合的方式支持可合作取消。您可以将单个标记传递给任意数量的方法调用,当该标记接收取消请求(通过创建标记的 CancellationTokenSource)时,所有执行中的操作都会看到该取消请求。这种方式与 WinRT 异步所用的方式略有不同,后者是使每个 IAsyncInfo 单独公开其 Cancel 方法。在此前提下,我们如何安排在 IAsyncOperation 上的 Cancel 调用接收在传递给 DownloadStringAsyncInternal 的 CancellationToken 上请求的取消?在这种情况下,AsAsyncOperation 将不起作用:
public static IAsyncOperation<string> DownloadStringAsync(string uri)
{
     return DownloadStringAsyncInternal(uri, … /* what goes here?? */)
            .AsAsyncOperation();
}为了解何时请求取消 IAsyncOperation,该实例需要以某种方式通知监听器其 Cancel 方法已被调用,例如通过请求取消要传递给 DownloadStringAsyncInternal 的 CancellationToken。但在经典的“第 22 条军规”中,要想获得在其上调用 AsAsyncOperation 的 Task,我们必须先调用 DownloadStringAsyncInternal,而此时我们就应该已经需要提供原本希望 AsAsyncOperation 提供的 CancellationToken 了。
有多种方法可以解决这个难题,其中包括 AsyncInfo.Run 所使用的解决方案。Run 方法负责构建 IAsyncOperation,并且创建用于请求取消 CancellationToken 的实例,而当调用异步操作的 Cancel 方法时,它也会创建该实例。然后,当调用由用户提供的已传递给 Run 的委派时,该方法会传入此标记,从而避免前面提到的难题:
public static IAsyncOperation<string> DownloadStringAsync(string uri)
{
    return AsyncInfo.Run(cancellationToken =>
        DownloadStringAsyncInternal(uri, cancellationToken));
}Lambda 和匿名方法
AsyncInfo.Run 简化了使用 lambda 函数和匿名方法来实施 WinRT 异步方法的过程。
例如,如果我们还没有 DownloadStringAsyncInternal 方法,我们也许会通过以下方式实施该方法和 DownloadStringAsync:
public static IAsyncOperation<string> DownloadStringAsync(string uri)
{
    return AsyncInfo.Run(delegate(CancellationToken cancellationToken)
    {
        return DownloadStringAsyncInternal(uri, cancellationToken));
    });
}
private static async Task<string> DownloadStringAsyncInternal(
    string uri, CancellationToken cancellationToken)
{
    var response = await new HttpClient().GetAsync(
        uri, cancellationToken);
    response.EnsureSuccessStatusCode();
    return await response.Content.ReadAsStringAsync();
}通过利用 C# 和 Visual Basic 支持来编写异步匿名方法,我们可以通过将这两种方法结合成一种来简化实施过程:
public static IAsyncOperation<string> DownloadStringAsync(string uri)
{
    return AsyncInfo.Run(async delegate(CancellationToken cancellationToken)
    {
        var response = await new HttpClient().GetAsync(
            uri, cancellationToken);
        response.EnsureSuccessStatusCode();
        return await response.Content.ReadAsStringAsync();
    });
}    进度
AsyncInfo.Run 还通过 WinRT 异步方法提供进度报告支持。
放弃使用 DownloadStringAsync 方法返回 IAsyncOperation 的想法,假设我们希望该方法返回 IAsyncOperationWithProgress
public static IAsyncOperationWithProgress<string,int> DownloadStringAsync(string uri);
DownloadStringAsync 现在可以提供包含完整数据的进度更新,例如,用户可以设置一个委派作为接口的 Progress 属性,以接收进度变更通知。
AsyncInfo.Run 提供接受 Func
public static IAsyncOperationWithProgress<string,int> DownloadStringAsync(string uri)
{
    return AsyncInfo.Run(async delegate(
        CancellationToken cancellationToken, IProgress<int> progress)
    {
        progress.Report(0);
        try
        {
            var response = await new HttpClient().GetAsync(uri, cancellationToken);
            progress.Report(50);
            response.EnsureSuccessStatusCode();
            return await response.Content.ReadAsStringAsync();
        }
        finally { progress.Report(100); }
    });
}
后台真相
要透彻理解所有这些工作原理,让我们来探讨一下 AsAsyncOperation 和 AsyncInfo.Run 的实施。它们与 .NET 4.5 中存在的实施不同,功能也没有那么强大。它们只是一些近似实施,帮助您深入了解后台的工作方式,并不适用于实际生产。
AsAsyncOperation
AsAsyncOperation 方法调用 Task,并返回 IAsyncOperation:
public static IAsyncOperation<TResult> AsAsyncOperation<TResult>(
    this Task<TResult> source);要实施此方法,我们需要创建一个实施 IAsyncOperation 并封装所提供的任务的类型。
public static IAsyncOperation<TResult> AsAsyncOperation<TResult>(
    this Task<TResult> source)
{
    return new TaskAsAsyncOperationAdapter<TResult>(source);
}
internal class TaskAsAsyncOperationAdapter<TResult> : IAsyncOperation<TResult>
{
    private readonly Task<TResult> m_task;
    public TaskAsAsyncOperationAdapter(Task<TResult> task) { m_task = task; }
    ...
}此类型上的每个接口方法实施都会委派给所封装任务的功能。万丈高楼平地起,我们先来了解最简单的成员。
首先,IAsyncInfo.Close 方法应该会彻底清除已完成的异步操作所使用过的任何资源。由于我们没有此类资源(我们的对象只封装了一个任务),因此我们的实施是空的:
public void Close() { /* NOP */ }
使用 IAsyncInfo.Cancel 方法,用户可以请求取消执行的异步操作。这只是一个单纯的请求,不会以任何方式强制操作退出。我们仅使用 CancellationTokenSource 存储发生的请求:
private readonly CancellationTokenSource m_canceler =
    new CancellationTokenSource();
public void Cancel() { m_canceler.Cancel(); }IAsyncInfo.Status 属性返回一个 AsyncStatus 来代表操作相对于其异步生命周期而言的当前状态。这可能是以下四个值之一:Started、Completed、Error 或 Canceled。大部分时候,我们可以仅委派给底层 Task 的 Status 属性,并将其返回的 TaskStatus 映射到所需的 AsyncStatus:
如果 Task 尚未完成就已请求了取消,我们就需要返回 Canceled 而不是 Started。这意味着,尽管 TaskStatus.Canceled 只是一种终端状态,但 AsyncStatus.Canceled 既可以是终端状态,也可以是非终端状态,因为 IAsyncInfo 可以以 Canceled 状态结束,或者 IAsyncInfo 可以从 Canceled 状态转变为 AsyncStatus.Completed 或 AsyncStatus.Error 状态。
public AsyncStatus Status
{
    get
    {
        switch (m_task.Status)
        {
            case TaskStatus.RanToCompletion:
                return AsyncStatus.Completed;
            case TaskStatus.Faulted:
                return AsyncStatus.Error;
            case TaskStatus.Canceled:
                return AsyncStatus.Canceled;
            default:
                return m_canceler.IsCancellationRequested ?
                    AsyncStatus.Canceled :
                    AsyncStatus.Started;
        }
    }
}IAsyncInfo.Id 属性会为操作返回一个 UInt32 标识符。由于 Task 本身已经公开了一个这样的标识符(例如 Int32),我们可以仅仅通过委派到底层 Task 属性来实施此属性:
public uint Id { get { return (uint)m_task.Id; } }WinRT 定义 IAsyncInfo.ErrorCode 属性以返回 HResult。但 CLR 会在内部将 WinRT HResult 映射到 .NET Exception,该过程通过托管投影的方式实现。Task 自身也会公开 Exception 属性,因此我们可以直接委派给它:如果 Task 以 Faulted 状态结束,我们返回其第一个异常(Task 可能会因多个异常出错,例如从 Task.WhenAll 返回的 Task),否则就返回 null:
public Exception ErrorCode
{
    get { return m_task.IsFaulted ? m_task.Exception.InnerException : null; }
}这就是实施 IAsyncInfo 的过程。现在,我们需要实施 IAsyncOperation 所提供的另外两个成员:GetResults 和 Completed。
操作成功完成或者发生异常后,用户调用 GetResults。在前一种情况下,它会返回计算出的结果,在后一种情况下,它会抛出相关的异常。如果操作以 Canceled 状态结束,或者操作尚未结束,则不得调用 GetResults。因此,我们可以按如下所示实施 GetResults,依靠任务的 awaiter GetResult 方法来在操作成功时返回结果,或在任务以 Faulted 状态结束时传播相应的异常。
public TResult GetResults()
{
    switch (m_task.Status)
    {
        case TaskStatus.RanToCompletion:
        case TaskStatus.Faulted:
            return m_task.GetAwaiter().GetResult();
        default:
            throw new InvalidOperationException("Invalid GetResults call.");
    }
}最后,我们来看一下 Completed 属性。Completed 代表一个应在操作完成时调用的委派。如果在设置 Completed 时操作已完成,则提供的委派必须立即调用或加入计划。此外,用户只能设置一次该属性(尝试设置多次将导致异常)。操作完成后,实施必须将引用降至该委派,以避免内存泄漏。我们可以依靠 Task 的 ContinueWith 方法来实施此行为中的大部分,但是由于 ContinueWith 可以在同一 Task 实例中多次使用,我们需要手动实施更加严格的“设置一次”行为:
private AsyncOperationCompletedHandler<TResult> m_handler;
private int m_handlerSet;
public AsyncOperationCompletedHandler<TResult> Completed
{
    get { return m_handler; }
    set
    {
        if (value == null)
            throw new ArgumentNullException("value");
        if (Interlocked.CompareExchange(ref m_handlerSet, 1, 0) != 0)
            throw new InvalidOperationException("Handler already set.");
        m_handler = value;
        var sc = SynchronizationContext.Current;
        m_task.ContinueWith(delegate {
            var handler = m_handler;
            m_handler = null;
            if (sc == null)
                handler(this, this.Status);
            else
                sc.Post(delegate { handler(this, this.Status); }, null);
       }, CancellationToken.None,
          TaskContinuationOptions.ExecuteSynchronously,
          TaskScheduler.Default);
    }
}
如此一来,我们的 AsAsyncOperation 实施就已完成,并且我们可以将任何返回 Task 的方法用作 IAsyncOperation 方法。
AsyncInfo.Run
那么,更高级的 AsyncInfo.Run 有何功能?
public static IAsyncOperation<TResult> Run<TResult>(
        Func<CancellationToken,Task<TResult>> taskProvider);为支持此 Run 重载,我们可以使用刚才创建的 TaskAsAsyncOperationAdapter 类型,并保持所有现有实施不变。事实上,我们只需要扩展其功能,使其可以以 Func
internal class TaskAsAsyncOperationAdapter<TResult> : IAsyncOperation<TResult>
{
    private readonly Task<TResult> m_task;
    public TaskAsAsyncOperationAdapter(Task<TResult> task) { m_task = task; }
    public TaskAsAsyncOperationAdapter(
        Func<CancellationToken,Task<TResult>> func)
    {
        m_task = func(m_canceler.Token);
    }
    ...
}
public static class AsyncInfo
{
    public static IAsyncOperation<TResult> Run<TResult>(
        Func<CancellationToken, Task<TResult>> taskProvider)
    {
        return new TaskAsAsyncOperationAdapter<TResult>(taskProvider);
    }
    …
}
这一实施进一步证实了其实不存在任何魔力。AsAsyncAction、AsAsyncOperation 和 AsyncInfo.Run 都不过是免去您编写样板代码之劳的辅助实施。
总结
至此,希望您已经透彻了解了 AsAsyncAction、AsAsyncOperation 和 AsyncInfo.Run 的功能:它们简化了调用 Task 或 Task 的过程,并将其公开为 IAsyncAction、IAsyncOperation、IAsyncActionWithProgress 或 IAsyncOperationWithProgress
将 .NET 任务作为 WinRT 异步操作公开的更多相关文章
- Windows store app[Part 4]:深入WinRT的异步机制
		接上篇Windows store app[Part 3]:认识WinRT的异步机制 WinRT异步机制回顾: IAsyncInfo接口:WinRT下异步功能的核心,该接口提供所有异步操作的基本功能,如 ... 
- 深入探究 WinRT 和 await
		在最近发布的使用 Windows 运行时中异步性来始终保持应用程序能够快速流畅地运行这篇博文中,包含了一些如何在 C# 和 Visual Basic 中使用 await 关键字的示例,允许开发人员在使 ... 
- 使用 Windows 运行时中异步性来始终保持应用程序能够快速流畅地运行
		转自:http://blogs.msdn.com/b/windowsappdev_cn/archive/2012/03/26/windows.aspx 人类的思维方式在本质上不是同步的,这直接影响着我 ... 
- C#客户端的异步操作
		上篇博客[用Asp.net写自己的服务框架] 我讲述了如何实现自己的服务框架,但我想很多人应该用过WebService这类服务框架,相比起来,似乎还缺少什么东西, 是的,我也感觉到了.比如:我可以很容 ... 
- Win8 WinRT将替换Win32 API程序员何去何从?
		win8 新引入了称为WinRT的核心API.支持使用C/C++..NET或JavaScript来开发Metro风格的应用.这些应用自动获得硬件加速和高级电源管理的功能.现有的Silverlight和 ... 
- [CLR via C#]26. 计算限制的异步操作
		一.CLR线程池基础 前面说过,创建和销毁线程是一个比较昂贵的操作,太多的线程也会浪费内存资源.由于操作系统必须调度可运行的线程并执行上下文切换,所以太多的线程还有损于性能.为了改善这个情况,CLR使 ... 
- 使用BackgroundWorker组件进行异步操作编程
		本文介绍了BackgroundWorker组件的功能及在基于事件的异步操作编程中的应用,并对组件的实现原理进行简述.在应用程序中,可能会遇到一些执行耗时的功能操作,比如数据下载.复杂计算及数据库事务等 ... 
- 【C#】C#线程_计算限制的异步操作
		目录结构: contents structure [+] 线程池简介 执行上下文(Execution Context) CancelTokenSource的使用 ThreadPool Task和Tas ... 
- Windows store app[Part 3]:认识WinRT的异步机制
		WinRT异步机制的诞生背景 当编写一个触控应用程序时,执行一个耗时函数,并通知UI更新,我们希望所有的交互过程都可以做出快速的反应.流畅的操作感变的十分重要. 在连接外部程序接口获取数据,操作本地数 ... 
随机推荐
- 《机器学习》第三章——LDA
			import numpy as np x=np.zeros((2,17)) y=np.zeros((1,17)) def dataload(filename): f=open(filename) ar ... 
- Swift GCD
			var queue: dispatch_queue_t = dispatch_get_main_queue()// 主线程 queue = dispatch_get_global_queue(DISP ... 
- 【javascript】利用jquery ajaxPrefilter防止ajax重复提交
			利用jquery ajaxPrefilter中断请求 var pendingRequests = {}; $.ajaxPrefilter(function( options, originalOpti ... 
- hexo+github部署
			废话不多少,接着上次配置的环境进行github部署. 拥有自己的github 如果还没有github的账号就注册一个吧,传送门:GitHub官网:http://www.github.com 创建一个创 ... 
- 今天需要做手机端访问的页面,所以把meta的整理一下。
			<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale= ... 
- WPF 程序无法触摸操作?我们一起来找原因和解决方法!
			WPF 自诞生以来就带着微软先生的傲慢.微软说 WPF 支持触摸,于是 WPF 就真的支持触摸了.对,我说的是"支持触摸",那种摸上去能点能动的:偶尔还能带点儿多指的炫酷效果.但是 ... 
- 接口测试基础——第7篇 简单的Python知识普及(二)之装饰器
			今天我们来学习python里面的“装饰器” 1.我在函数test运行前想先打印一些内容怎么办? def func(param): print u"打印的内容" para ... 
- MAMP环境配置
			命令行: 开启apache服务:sudo apachectl start 停止apache服务:sudo apachectl stop 重启服务:sudo apachectl restart 查看版本 ... 
- Linux 之 hugepage 大页内存理论
			HugePages是通过使用大页内存来取代传统的4kb内存页面,使得管理虚拟地址数变少,加快了从虚拟地址到物理地址的映射以及通过摒弃内存页面的换入换出以提高内存的整体性能.尤其是对于8GB以上的内存以 ... 
- centos6.6安装php5.3.3(2015/3/4)
			问题:centos6.6因要升级mysql5.5所以yum重新更新了源,导致按照原来lamp环境安装步骤,安装php时一直找webtitic源,php5.3.24 而且一直无法安装下去 利用yum r ... 
