最近趁着项目的一段平稳期研读了不少书籍,其中《C#并发编程经典实例》给我的印象还是比较深刻的。当然,这可能是由于近段日子看的书大多嘴炮大于实际,如《Head First设计模式》《Cracking the coding interview》等,所以陡然见到一本打着“实例”旗号的书籍,还是挺让我觉得耳目一新。本着分享和加深理解的目的,我特地整理了一些笔记(主要是Web开发中容易涉及的内容,所以部分章节如数据流,RX等我看了看就直接跳过了),以供审阅学习。语言和技术的魅力,真是不可捉摸
一直以来都有一种观点是实现底层架构,编写驱动和引擎,或者是框架和工具开发的才是高级开发人员,做上层应用的人仅仅是“码农”,其实能够利用好平台提供的相关类库,而不是全部采用底层技术自己实现,开发出高质量,稳定的应用程序,对技术能力的考验并不低于开发底层库,如TPL,async,await等。
static async Task<string> DownloadStringWithRetries(string uri)
{
using (var client = new HttpClient())
{
// 第 1 次重试前等 1 秒,第 2 次等 2 秒,第 3 次等 4 秒。
var nextDelay = TimeSpan.FromSeconds(1);
for (int i = 0; i != 3; ++i)
{
try
{
return await client.GetStringAsync(uri);
}
catch
{ }
await Task.Delay(nextDelay);
nextDelay = nextDelay + nextDelay;
}
// 最后重试一次,以便让调用者知道出错信息。
return await client.GetStringAsync(uri);
}
}
static void ProcessArray(double[] array)
{
Parallel.Invoke(
() => ProcessPartialArray(array, 0, array.Length / 2),
() => ProcessPartialArray(array, array.Length / 2,array.Length));
}
static void ProcessPartialArray(double[] array, int begin, int end)
{
// 计算密集型的处理过程 ...
}
这里附上一个ABP中实现的可操作AsyncHelper类,就是基于AsyncContext实现
/// <summary>
/// Provides some helper methods to work with async methods.
/// </summary>
public static class AsyncHelper
{
/// <summary>
/// Checks if given method is an async method.
/// </summary>
/// <param name="method">A method to check</param>
public static bool IsAsyncMethod(MethodInfo method)
{
return (
method.ReturnType == typeof(Task) ||
(method.ReturnType.IsGenericType && method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>))
);
}
/// <summary>
/// Runs a async method synchronously.
/// </summary>
/// <param name="func">A function that returns a result</param>
/// <typeparam name="TResult">Result type</typeparam>
/// <returns>Result of the async operation</returns>
public static TResult RunSync<TResult>(Func<Task<TResult>> func)
{
return AsyncContext.Run(func);
}
/// <summary>
/// Runs a async method synchronously.
/// </summary>
/// <param name="action">An async action</param>
public static void RunSync(Func<Task> action)
{
AsyncContext.Run(action);
}
}
var instance=new Program.CreateAsync();
class Program
{
private Program()
{
}
private async Task<Program> InitializeAsync()
{
await Task.Delay(TimeSpan.FromSeconds(1));
return this;
}
public static Task<Program> CreateAsync()
{
var result = new Program();
return result.InitializeAsync();
}
}
class SharedData
{
public int Value { get; set; }
}
async Task ModifyValueAsync(SharedData data)
{
await Task.Delay(TimeSpan.FromSeconds(1));
data.Value = data.Value + 1;
}
// 警告:可能需要同步,见下面的讨论。
async Task<int> ModifyValueConcurrentlyAsync()
{
var data = new SharedData();
// 启动三个并发的修改过程。
var task1 = ModifyValueAsync(data);
var task2 = ModifyValueAsync(data);
var task3 = ModifyValueAsync(data);
await Task.WhenAll(task1, task2, task3);
return data.Value;
}
本例中,启动了三个并发运行的修改过程。需要同步吗?答案是“看情况”。如果能确定 这个方法是在 GUI 或 ASP.NET 上下文中调用的(或同一时间内只允许一段代码运行的任 何其他上下文),那就不需要同步,因为这三个修改数据过程的运行时间是互不相同的。 例如,如果它在 GUI 上下文中运行,就只有一个 UI 线程可以运行这些数据修改过程,因 此一段时间内只能运行一个过程。因此,如果能够确定是“同一时间只运行一段代码”的 上下文,那就不需要同步。但是如果从线程池线程(如 Task.Run)调用这个方法,就需要同步了。在那种情况下,这三个数据修改过程会在独立的线程池线程中运行,并且同时修改 data.Value,因此必须同步地访问 data.Value。
(7) 实用技巧
static int _simpleValue;
static readonly Lazy<Task<int>> MySharedAsyncInteger =
new Lazy<Task<int>>(() =>
Task.Run(async () =>
{
await Task.Delay(TimeSpan.FromSeconds(2));
return _simpleValue++;
}));
async Task GetSharedIntegerAsync()
{
int sharedValue = await MySharedAsyncInteger.Value;
}