Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/336.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 将传入的并行请求打包为一个请求的模式_C#_Performance_Optimization_Design Patterns_Parallel Processing - Fatal编程技术网

C# 将传入的并行请求打包为一个请求的模式

C# 将传入的并行请求打包为一个请求的模式,c#,performance,optimization,design-patterns,parallel-processing,C#,Performance,Optimization,Design Patterns,Parallel Processing,假设我们有许多随机进入的线程并行访问同一资源。要访问资源,线程需要获取锁。如果我们可以将N个传入线程打包到一个请求中,那么资源使用效率将提高N倍。此外,我们还需要尽快回答个人请求。在C#中实现这一点的最佳方式/模式是什么 目前我有这样的想法: //batches lock var ilock = ModifyBatch.GetTableDeleteBatchLock(table_info.Name); lock (ilock) { // put the request into requ

假设我们有许多随机进入的线程并行访问同一资源。要访问资源,线程需要获取锁。如果我们可以将N个传入线程打包到一个请求中,那么资源使用效率将提高N倍。此外,我们还需要尽快回答个人请求。在C#中实现这一点的最佳方式/模式是什么

目前我有这样的想法:

//batches lock
var ilock = ModifyBatch.GetTableDeleteBatchLock(table_info.Name);
lock (ilock)
{
    // put the request into requests batch
    if (!ModifyBatch._delete_batch.ContainsKey(table_info.Name))
    {
        ModifyBatch._delete_batch[table_info.Name] = new DeleteData() { Callbacks = new List<Action<string>>(), ids = ids };
    }
    else
    {
        ModifyBatch._delete_batch[table_info.Name].ids.UnionWith(ids);
    }
    //this callback will get called once the job is done by a thread that will acquire resource lock
    ModifyBatch._delete_batch[table_info.Name].Callbacks.Add(f =>
    {
        done = true;
        error = f;
    });
}

bool lockAcquired = false;
int maxWaitMs = 60000;
DeleteData _delete_data = null;

//resource lock
var _write_lock = GetTableWriteLock(typeof(T).Name);
try
{
    DateTime start = DateTime.Now;
    while (!done)
    {
        lockAcquired = Monitor.TryEnter(_write_lock, 100);
        if (lockAcquired)
        {
            if (done) //some other thread did our job
                            {
                Monitor.Exit(_write_lock);
                lockAcquired = false;
                break;
            }
            else
            {
                break;
            }
        }
        Thread.Sleep(100);
        if ((DateTime.Now - start).TotalMilliseconds > maxWaitMs)
        {
            throw new Exception("Waited too long to acquire write lock?");
        }
    }
    if (done) //some other thread did our job
    {
        if (!string.IsNullOrEmpty(error))
        {
            throw new Exception(error);
        }
        else
        {
            return;
        }
    }

    //not done, but have write lock for the table
    lock (ilock)
    {
        _delete_data = ModifyBatch._delete_batch[table_info.Name];
        var oval = new DeleteData();
        ModifyBatch._delete_batch.TryRemove(table_info.Name, out oval);
    }
    if (_delete_data.ids.Any())
    {
        //doing the work with resource 
    }
    foreach (var cb in _delete_data.Callbacks)
    {
        cb(null);
    }
}
catch (Exception ex)
{
    if (_delete_data != null)
    {
        foreach (var cb in _delete_data.Callbacks)
        {
            cb(ex.Message);
        }
    }
    throw;
}
finally
{
    if (lockAcquired)
    {
        Monitor.Exit(_write_lock);
    }
}
//批处理锁
var ilock=ModifyBatch.GetTableDeleteBatchLock(表信息名称);
锁(ilock)
{
//将请求放入请求批处理
如果(!ModifyBatch.\u delete\u batch.ContainsKey(表信息名称))
{
ModifyBatch.\u delete\u batch[table\u info.Name]=new DeleteData(){Callbacks=new List(),ids=ids};
}
其他的
{
ModifyBatch.\u delete\u batch[表\u info.Name].ids.UnionWith(ids);
}
//一旦任务由获取资源锁的线程完成,就会调用此回调
ModifyBatch.\u delete\u batch[表信息.名称].回调.添加(f=>
{
完成=正确;
误差=f;
});
}
布尔=假;
int-maxWaitMs=60000;
DeleteData _delete_data=null;
//资源锁
var\u write\u lock=GetTableWriteLock(typeof(T).Name);
尝试
{
DateTime start=DateTime.Now;
而(!完成)
{
lockAcquired=Monitor.TryEnter(\u write\u lock,100);
如果(已获得)
{
如果(完成)//其他线程完成了我们的工作
{
监视器。退出(写入锁定);
错误=错误;
打破
}
其他的
{
打破
}
}
睡眠(100);
if((DateTime.Now-start).totalmillizes>maxWaitMs)
{
抛出新异常(“等待获取写锁的时间太长?”);
}
}
如果(完成)//其他线程完成了我们的工作
{
如果(!string.IsNullOrEmpty(错误))
{
抛出新异常(错误);
}
其他的
{
返回;
}
}
//未完成,但具有表的写锁
锁(ilock)
{
_delete_data=ModifyBatch._delete_batch[表信息.名称];
var oval=新的DeleteData();
ModifyBatch.\u delete\u batch.TryRemove(表\u info.Name,out椭圆形);
}
if(_delete_data.ids.Any())
{
//用资源做工作
}
foreach(var cb in_delete_data.Callbacks)
{
cb(空);
}
}
捕获(例外情况除外)
{
if(_delete_data!=null)
{
foreach(var cb in_delete_data.Callbacks)
{
cb(例如信息);
}
}
投掷;
}
最后
{
如果(已获得)
{
监视器。退出(写入锁定);
}
}

如果可以在当前请求的范围之外处理任务,即将其排队等待以后处理,则可以考虑如下顺序1:

实施资源锁定(监视器)和任务列表

  • 对于每个请求:

  • 锁定列表,将当前任务添加到列表,记住列表中的任务数,解锁列表

  • 设法弄到锁

  • 如果不成功:

    • 如果列表中的任务数<阈值X,则返回
    • 否则获取锁(将阻止)
  • 锁定列表,将其内容移动到临时列表,解锁列表

  • 如果临时列表不为空

    • 执行临时列表中的任务

    • 重复步骤5

  • 松开锁

  • 第一个请求将贯穿整个序列。如果第一个请求仍在执行,则后续请求将在步骤4短路

    调整最佳阈值X(或将其更改为基于时间的阈值)


    1如果您需要等待请求范围内的任务,则需要稍微延长流程:

    向任务类添加两个字段:完成标志和异常

    在步骤4,返回之前,等待任务完成(
    Monitor.wait
    ),直到其完成标志变为
    true
    。如果异常不是
    null
    ,则抛出它


    在步骤6中,对于每个任务,设置完成标志和可选的异常,然后通知服务员(
    Monitor.PulseAll
    )。

    “如果我们可以将N个传入线程打包到一个请求中,资源使用效率将提高N倍”-为什么?因为我们必须维护索引(资源)一次迭代中的修改比N次迭代中的修改要便宜。。。许多的命名约定。保持一致,让你(和其他人)更容易阅读/maintain@pinkfloydx33哈哈,坏习惯在我的案例中持续很长时间。请求必须等到成功/失败