我真的无法弄清楚光纤如何解决您的问题,因为他们基本上不提供减少共享内存资源争用的方法。
我宁愿专注于战略,以减少对资源的争夺,减少重复计算,减少线程资源使用与asynhronous处理。
在所有请求处理之上使用全局锁定基本上将所有处理都减少为单个活动线程。作为替代方法,您可以尝试减少每个资源仅使用锁的锁定。
Disclamer:这里给出的示例代码绝不是生产质量,它仅仅是为了说明这些概念。
减少争
你能想出颗粒状锁定策略,当你在特定的操作问题锁定数据的只有部分区域。
以下是排序游戏的一个示例,它定义了简单的规则: 每个玩家在列表中抓取一个项目,并在下一个项目中交换,如果左侧项目小于右侧。 游戏在所有项目排序后结束。 没有人赢,只是很有趣。
using System;
using System.Threading;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
var game = new SortingGame();
var random = new Random(234);
// Simulate few concurrent players.
for (var i = 0; i < 3; i++)
{
ThreadPool.QueueUserWorkItem(o =>
{
while (!game.IsSorted())
{
var x = random.Next(game.Count() - 1);
game.PlayAt(x);
DumpGame(game);
};
});
}
Thread.Sleep(4000);
DumpGame(game);
}
static void DumpGame(SortingGame game)
{
var items = game.GetBoardSnapshot();
Console.WriteLine(string.Join(",", items));
}
}
class SortingGame
{
List<int> items;
List<object> lockers;
// this lock is taken for the entire board to guard from inconsistent reads.
object entireBoardLock = new object();
public SortingGame()
{
const int N = 10;
// Initialize a game with items in random order
var random = new Random(1235678);
var setup = Enumerable.Range(0, N).Select(i => new { x = i, position = random.Next(0, 100)}).ToList();
items = setup.OrderBy(i => i.position).Select(i => i.x).ToList();
lockers = Enumerable.Range(0, N).Select(i => new object()).ToList();
}
public int Count()
{
return items.Count;
}
public bool IsSorted()
{
var currentBoard = GetBoardSnapshot();
var pairs = currentBoard.Zip(currentBoard.Skip(1), (a, b) => new { a, b});
return pairs.All(p => p.a <= p.b);
}
public IEnumerable<int> GetBoardSnapshot()
{
lock (entireBoardLock)
return new List<int>(items);
}
public void PlayAt(int x)
{
// Find the resource lockers for the two adjacent cells in question
var locker1 = GetLockForCell(x);
var locker2 = GetLockForCell(x + 1);
// It's important to lock the resources in a particular order, same for all the contending writers and readers.
// These can last for a long time, but are granular,
// so the contention is greatly reduced.
// Try to remove one of the following locks, and notice the duplicate items in the result
lock (locker1)
lock (locker2)
{
var a = items[x];
var b = items[x + 1];
if (a > b)
{
// Simulate expensive computation
Thread.Sleep(100);
// Following is a lock to protect from incorrect game state read
// The lock lasts for a very short time.
lock (entireBoardLock)
{
items[x] = b;
items[x + 1] = a;
}
}
}
}
object GetLockForCell(int x)
{
return lockers[x];
}
}
消除重复计算
如果你需要一些昂贵的计算是最新的,但不依赖于特定的请求,试图计算它的每个请求将只是浪费资源。
如果计算已经为另一个请求启动,以下方法允许跳过重复计算。
这是一个从高速缓存不同,因为你实际上得到最好的结果,计算在一个时间框架是这样的:
void Main()
{
for (var i = 0; i < 100; i++)
{
Thread.Sleep(100);
var j = i;
ThreadPool.QueueUserWorkItem((o) => {
// In this example, the call is blocking becase of the Result property access.
// In a real async method you would be awaiting the result.
var result = computation.Get().Result;
Console.WriteLine("{0} {1}", j, result);
});
}
}
static ParticularSharedComputation computation = new ParticularSharedComputation();
abstract class SharedComputation
{
volatile Task<string> currentWork;
object resourceLock = new object();
public async Task<string> Get()
{
Task<string> current;
// We are taking a lock here, but all the operations inside a lock are instant.
// Actually we are just scheduling a task to run.
lock (resourceLock)
{
if (currentWork == null)
{
Console.WriteLine("Looks like we have to do the job...");
currentWork = Compute();
currentWork.ContinueWith(t => {
lock (resourceLock)
currentWork = null;
});
}
else
Console.WriteLine("Someone is already computing. Ok, will wait a bit...");
current = currentWork;
}
return await current;
}
protected abstract Task<string> Compute();
}
class ParticularSharedComputation : SharedComputation
{
protected override async Task<string> Compute()
{
// This method is thread safe if it accesses only it's instance data,
// as the base class allows only one simultaneous entrance for each instance.
// Here you can safely access any data, local for the instance of this class.
Console.WriteLine("Computing...");
// Simulate a long computation.
await Task.Delay(2000);
Console.WriteLine("Computed.");
return DateTime.Now.ToString();
}
}
围棋异步,而不是多线程
即使你正在做的多线程,你可能会浪费线程资源,而且线程实际上很昂贵,因为为每个线程分配了栈内存并且由于上下文切换。
设计良好的异步应用程序实际上使用的线程数与系统中的CPU核数一样多。
研究让你的应用程序异步,而不仅仅是多线程。
在堆栈溢出中,有一些关于光纤的有趣讨论,请尝试搜索他们发送给MSDN几篇文章的“c#使用光纤”,这可能会有所帮助。 –
@Sabrina_cs - 感谢您的意见。到目前为止,我已阅读了关于此主题的一些讨论,并且可能会继续这样做。目前我正在努力的是两者之间的明确比较。再次感谢您的意见。 –
纤维是一种复杂的优化,它不会做任何事情来解决您的任务问题,即如何处理共享数据。一个巨大的锁是一个不好的解决方案,因为它不能缩放,所以你需要更好的锁定和所有必要的。 – Voo