Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/asp.net/31.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# 在Azure中锁定模拟_C#_Asp.net_Multithreading_Azure_Azure Web Roles - Fatal编程技术网

C# 在Azure中锁定模拟

C# 在Azure中锁定模拟,c#,asp.net,multithreading,azure,azure-web-roles,C#,Asp.net,Multithreading,Azure,Azure Web Roles,我需要在Azure web实例中实现一些线程结果逻辑。 我有一些这样的代码: lock (_bookingLock) { // Check for a free time bool isTimeFree = _scheduleService.IsTimeFree(dateTimeGuidId); //* race condition here if (isTimeFree) { // Make a booking. So this time

我需要在Azure web实例中实现一些线程结果逻辑。 我有一些这样的代码:

lock (_bookingLock)
{
    // Check for a free time
    bool isTimeFree = _scheduleService.IsTimeFree(dateTimeGuidId);
    //* race condition here
    if (isTimeFree)
    {
        // Make a booking. So this time is busy
        newBookingId = _paymentService.CreateBooking(dateTimeGuidId).ToString();
    }
}

但是我不能在多实例环境中使用
lock
,也不能忽略lock,因为
*
中存在竞争条件。这里最好的方法是什么?

我强烈建议通过一系列消息对此进行建模。您可以通过Azure服务总线发送消息(命令)来创建预订。只有一个消费者会处理该消息,您不需要“锁定”。此外,还可以扩展到多个使用者,以便可以同时处理多个命令。事件还可用于通知消费者状态的变化(如预订已创建或更新),并执行他们需要执行的操作。

我强烈建议通过一系列消息对此进行建模。您可以通过Azure服务总线发送消息(命令)来创建预订。只有一个消费者会处理该消息,您不需要“锁定”。此外,还可以扩展到多个使用者,以便可以同时处理多个命令。事件还可以用于通知消费者状态的更改(如创建或更新了预订),并执行他们需要执行的操作。

我决定使用blob租约。我升级到使用Azure存储客户端版本2或3,并编写了一个附加方法。以下是完整代码:

using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.WindowsAzure.Storage.Shared.Protocol;
using Microsoft.WindowsAzure.Storage.Blob.Protocol;
using System.Configuration;

namespace TerminalManager.Domain.Foundation.BlobLeases
{
    public class AutoRenewLease : IDisposable
    {
        public bool HasLease { get { return leaseId != null; } }

        AccessCondition _accessCondition;
        private CloudBlockBlob blob;
        private string leaseId;
        private Thread renewalThread;
        private bool disposed = false;

        public static void DoOnce(CloudBlockBlob blob, Action action) { DoOnce(blob, action, TimeSpan.FromSeconds(5)); }
        public static void DoOnce(CloudBlockBlob blob, Action action, TimeSpan pollingFrequency)
        {
            // blob.Exists has the side effect of calling blob.FetchAttributes, which populates the metadata collection
            while (!blob.Exists() || blob.Metadata["progress"] != "done")
            {
                using (var arl = new AutoRenewLease(blob))
                {
                    if (arl.HasLease)
                    {
                        action();
                        blob.Metadata["progress"] = "done";
                        AccessCondition ac = new AccessCondition();
                        ac.LeaseId = arl.leaseId;
                        blob.SetMetadata(ac);
                    }
                    else
                    {
                        Thread.Sleep(pollingFrequency);
                    }
                }
            }
        }

        /// <summary>
        /// Выполнить последовательно
        /// </summary>
        /// <param name="lockBlobName">имя блоба - просто буквы</param>
        /// <param name="action"></param>
        /// <param name="cnStrName">из конфига</param>
        /// <param name="containerName">из конфига</param>
        /// <param name="pollingFrequency"></param>
        public static void DoConsequence(string lockBlobName, Action action, 
            string cnStrName = "StorageConnectionString", 
            string containerName = "leasesContainer", TimeSpan? pollingFrequency = null)
        {
            //http://www.windowsazure.com/en-us/develop/net/how-to-guides/blob-storage/

            // Формат пути к блобу
            //http://<storage account>.blob.core.windows.net/<container>/<blob>
            // Блобовский аккаунт
            var account = CloudStorageAccount.Parse(ConfigurationManager.ConnectionStrings[cnStrName].ConnectionString); //CloudStorageAccount.Parse("UseDevelopmentStorage=true"); // Не работает на SDK 2.2 // or your real connection string
            var blobs = account.CreateCloudBlobClient();
            // Контейнер - типа папки
            var container = blobs
                .GetContainerReference(ConfigurationManager.AppSettings[containerName]);
            container.CreateIfNotExists();

            var blob = container.GetBlockBlobReference(lockBlobName);


            bool jobDone = false;

            while (!jobDone)
            {
                using (var arl = new AutoRenewLease(blob))
                {
                    if (arl.HasLease)
                    {
                        // Some Sync Work here 
                        action();
                        jobDone = true;
                    }
                    else
                    {
                        Thread.Sleep(pollingFrequency ?? TimeSpan.FromMilliseconds(300));
                    }
                }
            }
        }

        public static void DoEvery(CloudBlockBlob blob, TimeSpan interval, Action action)
        {
            while (true)
            {
                var lastPerformed = DateTimeOffset.MinValue;
                using (var arl = new AutoRenewLease(blob))
                {
                    if (arl.HasLease)
                    {
                        blob.FetchAttributes();
                        DateTimeOffset.TryParseExact(blob.Metadata["lastPerformed"], "R", CultureInfo.CurrentCulture, DateTimeStyles.AdjustToUniversal, out lastPerformed);
                        if (DateTimeOffset.UtcNow >= lastPerformed + interval)
                        {
                            action();
                            lastPerformed = DateTimeOffset.UtcNow;
                            blob.Metadata["lastPerformed"] = lastPerformed.ToString("R");
                            AccessCondition ac = new AccessCondition();
                            ac.LeaseId = arl.leaseId;
                            blob.SetMetadata(ac);
                        }
                    }
                }
                var timeLeft = (lastPerformed + interval) - DateTimeOffset.UtcNow;
                var minimum = TimeSpan.FromSeconds(5); // so we're not polling the leased blob too fast
                Thread.Sleep(
                    timeLeft > minimum
                    ? timeLeft
                    : minimum);
            }
        }

        public AutoRenewLease(CloudBlockBlob blob)
        {
            this.blob = blob;
            blob.Container.CreateIfNotExists();
            try
            {
                if (!blob.Exists())
                {
                    blob.UploadFromByteArray(new byte[0], 0, 0, AccessCondition.GenerateIfNoneMatchCondition("*"));// new BlobRequestOptions { AccessCondition = AccessCondition.IfNoneMatch("*") });
                }
            }
            catch (StorageException e)
            {
                if (e.RequestInformation.HttpStatusCode != (int)HttpStatusCode.PreconditionFailed // 412 from trying to modify a blob that's leased
                    && e.RequestInformation.ExtendedErrorInformation.ErrorCode != BlobErrorCodeStrings.BlobAlreadyExists
                    )
                {
                    throw;
                }
            }
            try
            {
                leaseId = blob.AcquireLease(TimeSpan.FromSeconds(60), null);
                _accessCondition = new AccessCondition { LeaseId = leaseId };
            }
            catch (Exception)
            {
                Trace.WriteLine("==========> Lease rejected! <==========");
            }

            if (HasLease)
            {
                renewalThread = new Thread(() =>
                {
                    while (true)
                    {
                        Thread.Sleep(TimeSpan.FromSeconds(40));
                        var ac = new AccessCondition();
                        ac.LeaseId = leaseId;
                        blob.RenewLease(ac);//.RenewLease(leaseId);
                    }
                });
                renewalThread.Start();
            }
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!disposed)
            {
                if (disposing)
                {
                    if (renewalThread != null)
                    {
                        renewalThread.Abort();
                        blob.ReleaseLease(_accessCondition);
                        renewalThread = null;
                    }
                }
                disposed = true;
            }
        }

        ~AutoRenewLease()
        {
            Dispose(false);
        }
    }
}

我决定使用blob租约。我升级到使用Azure存储客户端版本2或3,并编写了一个附加方法。以下是完整代码:

using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.WindowsAzure.Storage.Shared.Protocol;
using Microsoft.WindowsAzure.Storage.Blob.Protocol;
using System.Configuration;

namespace TerminalManager.Domain.Foundation.BlobLeases
{
    public class AutoRenewLease : IDisposable
    {
        public bool HasLease { get { return leaseId != null; } }

        AccessCondition _accessCondition;
        private CloudBlockBlob blob;
        private string leaseId;
        private Thread renewalThread;
        private bool disposed = false;

        public static void DoOnce(CloudBlockBlob blob, Action action) { DoOnce(blob, action, TimeSpan.FromSeconds(5)); }
        public static void DoOnce(CloudBlockBlob blob, Action action, TimeSpan pollingFrequency)
        {
            // blob.Exists has the side effect of calling blob.FetchAttributes, which populates the metadata collection
            while (!blob.Exists() || blob.Metadata["progress"] != "done")
            {
                using (var arl = new AutoRenewLease(blob))
                {
                    if (arl.HasLease)
                    {
                        action();
                        blob.Metadata["progress"] = "done";
                        AccessCondition ac = new AccessCondition();
                        ac.LeaseId = arl.leaseId;
                        blob.SetMetadata(ac);
                    }
                    else
                    {
                        Thread.Sleep(pollingFrequency);
                    }
                }
            }
        }

        /// <summary>
        /// Выполнить последовательно
        /// </summary>
        /// <param name="lockBlobName">имя блоба - просто буквы</param>
        /// <param name="action"></param>
        /// <param name="cnStrName">из конфига</param>
        /// <param name="containerName">из конфига</param>
        /// <param name="pollingFrequency"></param>
        public static void DoConsequence(string lockBlobName, Action action, 
            string cnStrName = "StorageConnectionString", 
            string containerName = "leasesContainer", TimeSpan? pollingFrequency = null)
        {
            //http://www.windowsazure.com/en-us/develop/net/how-to-guides/blob-storage/

            // Формат пути к блобу
            //http://<storage account>.blob.core.windows.net/<container>/<blob>
            // Блобовский аккаунт
            var account = CloudStorageAccount.Parse(ConfigurationManager.ConnectionStrings[cnStrName].ConnectionString); //CloudStorageAccount.Parse("UseDevelopmentStorage=true"); // Не работает на SDK 2.2 // or your real connection string
            var blobs = account.CreateCloudBlobClient();
            // Контейнер - типа папки
            var container = blobs
                .GetContainerReference(ConfigurationManager.AppSettings[containerName]);
            container.CreateIfNotExists();

            var blob = container.GetBlockBlobReference(lockBlobName);


            bool jobDone = false;

            while (!jobDone)
            {
                using (var arl = new AutoRenewLease(blob))
                {
                    if (arl.HasLease)
                    {
                        // Some Sync Work here 
                        action();
                        jobDone = true;
                    }
                    else
                    {
                        Thread.Sleep(pollingFrequency ?? TimeSpan.FromMilliseconds(300));
                    }
                }
            }
        }

        public static void DoEvery(CloudBlockBlob blob, TimeSpan interval, Action action)
        {
            while (true)
            {
                var lastPerformed = DateTimeOffset.MinValue;
                using (var arl = new AutoRenewLease(blob))
                {
                    if (arl.HasLease)
                    {
                        blob.FetchAttributes();
                        DateTimeOffset.TryParseExact(blob.Metadata["lastPerformed"], "R", CultureInfo.CurrentCulture, DateTimeStyles.AdjustToUniversal, out lastPerformed);
                        if (DateTimeOffset.UtcNow >= lastPerformed + interval)
                        {
                            action();
                            lastPerformed = DateTimeOffset.UtcNow;
                            blob.Metadata["lastPerformed"] = lastPerformed.ToString("R");
                            AccessCondition ac = new AccessCondition();
                            ac.LeaseId = arl.leaseId;
                            blob.SetMetadata(ac);
                        }
                    }
                }
                var timeLeft = (lastPerformed + interval) - DateTimeOffset.UtcNow;
                var minimum = TimeSpan.FromSeconds(5); // so we're not polling the leased blob too fast
                Thread.Sleep(
                    timeLeft > minimum
                    ? timeLeft
                    : minimum);
            }
        }

        public AutoRenewLease(CloudBlockBlob blob)
        {
            this.blob = blob;
            blob.Container.CreateIfNotExists();
            try
            {
                if (!blob.Exists())
                {
                    blob.UploadFromByteArray(new byte[0], 0, 0, AccessCondition.GenerateIfNoneMatchCondition("*"));// new BlobRequestOptions { AccessCondition = AccessCondition.IfNoneMatch("*") });
                }
            }
            catch (StorageException e)
            {
                if (e.RequestInformation.HttpStatusCode != (int)HttpStatusCode.PreconditionFailed // 412 from trying to modify a blob that's leased
                    && e.RequestInformation.ExtendedErrorInformation.ErrorCode != BlobErrorCodeStrings.BlobAlreadyExists
                    )
                {
                    throw;
                }
            }
            try
            {
                leaseId = blob.AcquireLease(TimeSpan.FromSeconds(60), null);
                _accessCondition = new AccessCondition { LeaseId = leaseId };
            }
            catch (Exception)
            {
                Trace.WriteLine("==========> Lease rejected! <==========");
            }

            if (HasLease)
            {
                renewalThread = new Thread(() =>
                {
                    while (true)
                    {
                        Thread.Sleep(TimeSpan.FromSeconds(40));
                        var ac = new AccessCondition();
                        ac.LeaseId = leaseId;
                        blob.RenewLease(ac);//.RenewLease(leaseId);
                    }
                });
                renewalThread.Start();
            }
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!disposed)
            {
                if (disposing)
                {
                    if (renewalThread != null)
                    {
                        renewalThread.Abort();
                        blob.ReleaseLease(_accessCondition);
                        renewalThread = null;
                    }
                }
                disposed = true;
            }
        }

        ~AutoRenewLease()
        {
            Dispose(false);
        }
    }
}

您是否与单个实体一起工作?换句话说,您是否通过将同一实体的属性更改为booked(如果可用)来更新该实体?否,我有一个包含空闲时间项目的表。我有一张预定的桌子。我检查一段时间是否没有预订,那么这段时间是免费的,可以预订。但是如果两个或更多的用户同时尝试预订空闲时间,那么他们将收到两次预订!在这种情况下,只有一个用户必须收到预订,另一个用户必须收到一条消息,表示此时正忙。大多数数据库都有事务,或者至少有原子检查和更新操作。围绕“azure事务”做一些研究我有azure SQL DB。我读过类似情况下的表锁定(整个表),但这是非常昂贵的操作。我想从数据层进行抽象,并在业务层同步我的操作。悲观并发控制(您正在尝试执行)或乐观并发控制。试着乐观一点,这可能更容易。你是在和一个实体合作吗?换句话说,您是否通过将同一实体的属性更改为booked(如果可用)来更新该实体?否,我有一个包含空闲时间项目的表。我有一张预定的桌子。我检查一段时间是否没有预订,那么这段时间是免费的,可以预订。但是如果两个或更多的用户同时尝试预订空闲时间,那么他们将收到两次预订!在这种情况下,只有一个用户必须收到预订,另一个用户必须收到一条消息,表示此时正忙。大多数数据库都有事务,或者至少有原子检查和更新操作。围绕“azure事务”做一些研究我有azure SQL DB。我读过类似情况下的表锁定(整个表),但这是非常昂贵的操作。我想从数据层进行抽象,并在业务层同步我的操作。悲观并发控制(您正在尝试执行)或乐观并发控制。试着乐观一点,这可能更容易,但这并不能保证有效。所有这些实际上都是为了保证对blob的租用访问。对于更广泛的问题,请阅读以下内容:这并不能保证有效。所有这些实际上都是为了保证对blob的租用访问。要想了解更广泛的问题,请阅读以下内容:并非如此。正确是要付出代价的。这也是为什么存在“最多一次”交付保证的服务总线队列的原因。正确是要付出代价的。这也是存在“最多一次”交付保证的服务总线队列的原因。