Memory locks

How to use the MemoryLock and NullLock components.

Key takeaways

ILock Interface that defines the main methods for locks.
MemoryLock Lock used to synchronize the execution of processes that use shared memory.
NullLock Dummy lock that performs no real actions.
acquireLock() Method used to acquire a lock by its key.
releaseLock() Method used to release a previously acquired lock by its key.

Introduction

This tutorial will help you understand how to use the MemoryLock and NullLock components. First, we will explore the ILock interface, which must be implemented by all locks. Next, we will learn the basic functionality of the MemoryLock class, and we will construct an example that will demonstrate how to use this type of lock. After this, we will learn what the NullLock is, how it differentiates from the MemoryLock, and when it should be used. At the end, we will summarize all the concepts learned.

ILock

This interface defines the main methods that each lock must implement, specifically acquireLock(), tryAcquireLock() and releaseLock(). As their names suggest, the first two methods are used to acquire a lock, while the third is used to release an acquired lock. Both components, MemoryLock and NullLock, implement this interface. NullLock implements it directly, and MemoryLock - via its parent class Lock. The following diagram summarizes their relations:

figure 2

MemoryLock

This component provides us with a lock that can be used to synchronize the execution of processes that use shared memory. In addition to its own methods, it inherits two important methods from the Lock class: acquireLock() and configure(). The following sections explain how to create, configure, acquire and release this type of lock.

Pre-requisites

In order to use this component, we need to import it first. The following example shows how to do this:

import { MemoryLock } from "pip-services3-components-nodex";
using PipServices3.Components.Lock;
import clock "github.com/pip-services3-gox/pip-services3-components-gox/lock"
import 'package:pip_services3_components/pip_services3_components.dart';
from pip_services3_components.lock.MemoryLock import MemoryLock
Not available

Lock creation

To create a lock, we just need to instantiate the MemoryLock class. The following line of code demonstrates this:

let lock = new MemoryLock();
var lockk = new MemoryLock();
lock := clock.NewMemoryLock()
var lock = MemoryLock();
lock = MemoryLock()
Not available

Lock configuration

Once we have an instance of a lock, we can configure the timeout (in milliseconds) to retry the lock acquisition via the configure() method. The default value is 100 (milliseconds), so, in the following example, we are overriding the default with a value of 200 (milliseconds):

let config = ConfigParams.fromTuples("retry_timeout", 200);
lock.configure(config);
var config = ConfigParams.FromTuples("retry_timeout", 200);
lockk.Configure(config);
config := cconf.NewConfigParamsFromTuples("retry_timeout", 200)
lock.Configure(context.Background(), config)
var lock = MemoryLock();
var config = ConfigParams.fromTuples(['retry_timeout', 200]);
lock.configure(config);
config = ConfigParams.from_tuples("retry_timeout", 200)
lock.configure(config)
Not available

Lock acquisition

After creation, a lock can be acquired through the acquireLock() method. This method accepts the correlationId, a key that identifies the lock, a lock timeout (milliseconds), and a lock acquisition timeout (milliseconds) as inputs. In the following example, we define the correlationId equal to “123”, a key with the value “mykey”, and we set both timeouts to 1000 milliseconds:

await lock.acquireLock("123", "mykey", 1000, 1000);
lockk.AcquireLock("123", "mykey", 1000, 1000);
lock.AcquireLock(context.Background(), "123", "mykey", 1000, 1000)
await lock.acquireLock('123', 'mykey', 1000, 1000);
lock.acquire_lock("123", "mykey", 1000, 1000, )
Not available

Lock release

Once done with, a lock can be released via the releaseLock() method. This method accepts the correlationId and the key of a previously acquired lock as inputs. In the following example, we use the same correlationId and key as in the previous example. In this manner, we can keep track of the process and identify the previously acquired lock.

await lock.releaseLock("123", "mykey");
lockk.ReleaseLock("123", "mykey");
lock.ReleaseLock(context.Background(), "123", "mykey")
await lock.releaseLock('123', 'mykey');
lock.release_lock("123", "mykey")
Not available

Example

Now that we have learned how to use the different methods available in this class, we can create an example that shows how they are used in practice.

In this example, we define a custom component with two methods. The first stores a value in memory, while the second retrieves the stored value and returns it. Both methods use a lock to manage their operations.

import { ConfigParams, Descriptor, IReferenceable, IReferences, References } from "pip-services3-commons-nodex";
import { ICache, MemoryCache, MemoryLock } from "pip-services3-components-nodex";

export class MyComponent implements IReferenceable {
    private _cache: ICache;
    private _lock: MemoryLock;

    public setReferences(references: IReferences): void {
        this._cache = references.getOneRequired(new Descriptor("*", "cache", "*", "*", "1.0"));
        this._lock = references.getOneRequired(new Descriptor("*", "lock", "*", "*", "1.0"));
    }
    
    public async storeResult(correlationId: string, param1: string): Promise<void> {
        // Lock
        this._lock.acquireLock(correlationId, "mykey", 1000, 1000);

        let config = ConfigParams.fromTuples("retry_timeout", 200);
        this._lock.configure(config);
        
        // Do processing
        // ...
        console.log("The stored value is " + param1);

        // Store result to cache async
        await  this._cache.store(correlationId, 'mykey', param1, 3600000);
    
        // Release lock async
        await this._lock.releaseLock(correlationId, 'mykey');
    }

    public async obtainResult(correlationId: string): Promise<any> {
        // Lock..
        this._lock.acquireLock(correlationId, "mykey", 1000, 1000);
        
        // Do processing
        // ...
        let result = this._cache.retrieve(correlationId, "mykey");
    
        // Release lock async
        await this._lock.releaseLock(correlationId, "mykey");

        return result
    }
}
    
    
// Use the component
let my_component = new MyComponent();
my_component.setReferences(References.fromTuples(
    new Descriptor("pip-services", "cache", "memory", "default", "1.0"), new MemoryCache(),
    new Descriptor("pip-services", "lock", "memory", "default", "1.0"), new MemoryLock(),
));

await my_component.storeResult(null, "param1");

let result = my_component.obtainResult(null);

console.log("The retrieved value is " + result);
using PipServices3.Commons.Config;
using PipServices3.Components.Lock;
using PipServices3.Components.Cache;
using PipServices3.Commons.Refer;

class MyComponent: IReferenceable
{
    private ICache _cache;
    private MemoryLock _lock;

    public void SetReferences(IReferences references)
    {
        _cache = references.GetOneRequired<ICache>(new Descriptor("*", "cache", "*", "*", "1.0"));
        _lock = references.GetOneRequired<MemoryLock>(new Descriptor("*", "lock", "*", "*", "1.0"));
    }


    public async Task StoreResultAsync(string correlationId, string param1) 
    {
        // Lock
        _lock.AcquireLock(correlationId, "mykey", 1000, 1000);

        var config = ConfigParams.FromTuples("retry_timeout", 200);
        this._lock.Configure(config);

        // Do processing
        // ...
        Console.WriteLine("The stored value is " + param1);

        // Store result to cache async
        await _cache.StoreAsync(correlationId, "mykey", param1, 3600000);

        // Release lock async
        _lock.ReleaseLock(correlationId, "mykey");
    }

    public async Task<string> ObtainResultAsync(string correlationId)
    {
        // Lock..
        this._lock.AcquireLock(correlationId, "mykey", 1000, 1000);

        // Do processing
        // ...
        var result = await this._cache.RetrieveAsync<string>(correlationId, "mykey");

        // Release lock async
        this._lock.ReleaseLock(correlationId, "mykey");

        return result;
    }
}

    
    
// Use the component
var my_component = new MyComponent();
my_component.SetReferences(References.FromTuples(
    new Descriptor("pip-services", "cache", "memory", "default", "1.0"), new MemoryCache(),
    new Descriptor("pip-services", "lock", "memory", "default", "1.0"), new MemoryLock()
));

await my_component.StoreResultAsync(null, "param1");
var result = my_component.ObtainResultAsync(null);

Console.WriteLine("The retrieved value is " + result);
import (
	"context"
	"fmt"

	cconf "github.com/pip-services3-gox/pip-services3-commons-gox/config"
	crefer "github.com/pip-services3-gox/pip-services3-commons-gox/refer"
	ccache "github.com/pip-services3-gox/pip-services3-components-gox/cache"
	clock "github.com/pip-services3-gox/pip-services3-components-gox/lock"
)

type MyComponent struct {
	cache ccache.ICache[any]
	lock  clock.MemoryLock
}

func (c *MyComponent) SetReferences(ctx context.Context, references crefer.IReferences) {
	res, descrErr := references.GetOneRequired(crefer.NewDescriptor("*", "cache", "*", "*", "1.0"))
	if descrErr != nil {
		panic(descrErr)
	}
	c.cache = res.(ccache.ICache[any])

	res, descrErr = references.GetOneRequired(crefer.NewDescriptor("*", "lock", "*", "*", "1.0"))
	if descrErr != nil {
		panic(descrErr)
	}
	c.lock = res.(clock.MemoryLock)
}

func (c *MyComponent) StoreResult(ctx context.Context, correlationId string, param1 string) {
	// Lock
	c.lock.AcquireLock(ctx, correlationId, "mykey", 1000, 1000)

	config := cconf.NewConfigParamsFromTuples("retry_timeout", 200)
	c.lock.Configure(ctx, config)

	// Do processing
	// ...
	fmt.Println("The stored value is " + param1)

	// Store result to cache async
	c.cache.Store(ctx, correlationId, "mykey", param1, 3600000)

	// Release lock async
	c.lock.ReleaseLock(ctx, correlationId, "mykey")
}

func (c *MyComponent) ObtainResult(ctx context.Context, correlationId string) any {
	// Lock..
	c.lock.AcquireLock(ctx, correlationId, "mykey", 1000, 1000)

	// Do processing
	// ...
	result, err := c.cache.Retrieve(ctx, correlationId, "mykey")

	// Release lock async
	c.lock.ReleaseLock(ctx, correlationId, "mykey")

	return result
}
    
    
func main() {
	ctx := context.Background()
	lock := clock.NewMemoryLock()
	config := cconf.NewConfigParamsFromTuples("retry_timeout", 200)
	lock.Configure(context.Background(), config)

	lock.AcquireLock(context.Background(), "123", "mykey", 1000, 1000)
	lock.ReleaseLock(context.Background(), "123", "mykey")

	// Use the component
	my_component := &MyComponent{}
	my_component.SetReferences(ctx, crefer.NewReferencesFromTuples(ctx,
		crefer.NewDescriptor("pip-services", "cache", "memory", "default", "1.0"), ccache.NewMemoryCache[any](),
		crefer.NewDescriptor("pip-services", "lock", "memory", "default", "1.0"), clock.NewMemoryLock(),
	))

	my_component.StoreResult(ctx, "123", "param1")

	result := my_component.ObtainResult(ctx, "123")

	fmt.Println("The retrieved value is " + result)
}
import 'package:pip_services3_commons/pip_services3_commons.dart';
import 'package:pip_services3_components/pip_services3_components.dart';

class MyComponent implements IReferenceable {
  late ICache _cache;
  late MemoryLock _lock;

  @override
  void setReferences(IReferences references) {
    _cache =
        references.getOneRequired(Descriptor('*', 'cache', '*', '*', '1.0'));
    _lock = references.getOneRequired(Descriptor('*', 'lock', '*', '*', '1.0'));
  }

  Future<void> storeResult(String? correlationId, String param1) async {
    // Lock
    await _lock.acquireLock(correlationId, 'mykey', 1000, 1000);

    var config = ConfigParams.fromTuples(['retry_timeout', 200]);
    _lock.configure(config);

    // Do processing
    // ...
    print('The stored value is ' + param1);

    // Store result to cache async
    await _cache.store(correlationId, 'mykey', param1, 3600000);

    // Release lock async
    await _lock.releaseLock(correlationId, 'mykey');
  }

  Future<String> obtainResult(String? correlationId) async {
    // Lock..
    await _lock.acquireLock(correlationId, 'mykey', 1000, 1000);

    // Do processing
    // ...
    var result = await _cache.retrieve(correlationId, 'mykey') as String;

    // Release lock async
    await _lock.releaseLock(correlationId, 'mykey');

    return result;
  }
}
    
    
// Use the component
var my_component = MyComponent();
my_component.setReferences(References.fromTuples([
  Descriptor('pip-services', 'cache', 'memory', 'default', '1.0'),
  MemoryCache(),
  Descriptor('pip-services', 'lock', 'memory', 'default', '1.0'),
  MemoryLock()
]));

await my_component.storeResult(null, 'param1');

var result = await my_component.obtainResult(null);

print('The retrieved value is ' + result);
from pip_services3_commons.refer import Descriptor, References, IReferences, IReferenceable
from pip_services3_components.cache import ICache, MemoryCache
from pip_services3_components.lock.ILock import ILock
from pip_services3_components.lock.MemoryLock import MemoryLock
from pip_services3_commons.config import ConfigParams

class MyComponent(IReferenceable):
    __cache: ICache
    __lock: ILock

    def set_references(self, references: IReferences):
        self.__cache = references.get_one_required(Descriptor("*", "cache", "*", "*", "1.0"))
        self.__lock = references.get_one_required(Descriptor("*", "lock", "*", "*", "1.0"))

    def store_result(self, correlation_id, param1):      

        print("The stored value is " + param1)

        # Lock
        self.__lock.acquire_lock(correlation_id, "mykey", 1000, 1000, )
        
        config = ConfigParams.from_tuples("retry_timeout", 200)
        self.__lock.configure(config)
        
        # Do processing
        # ...
       

        # Store result to cache async
        self.__cache.store(correlation_id, 'mykey', param1, 3600000)
    
        # Release lock async
        self.__lock.release_lock(correlation_id, 'mykey')

        
    def obtain_result(self, correlation_id):
        
         # Lock..
        self.__lock.acquire_lock(correlation_id, "mykey", 1000, 1000, )
        
        # Do processing
        # ...
        result = self.__cache.retrieve(correlation_id, "mykey")
    
        # Release lock async
        self.__lock.release_lock(correlation_id, "mykey")
    
        return result
    
    
# Use the component
my_component = MyComponent()
my_component.set_references(References.from_tuples(
    Descriptor("pip-services", "cache", "memory", "default", "1.0"), MemoryCache(),
    Descriptor("pip-services", "lock", "memory", "default", "1.0"), MemoryLock(),
))

my_component.store_result(None, "param1")

result = my_component.obtain_result(None)

print("The retrieved value is " + result)
Not available

And, after running the above code, we obtain the following results:

figure 1

NullLock

This component represents a dummy lock that produces no real results. As such, it can be used for testing purposes or in situations where a lock is required, but needs to be disabled. Another point worth mentioning is that this class doesn’t contain the configure() method. .

Pre-requisites

In order to use this component, we need to import it first. The following example shows how to do this:

import { NullLock } from "pip-services3-components-nodex";
using PipServices3.Components.Lock;
import clock "github.com/pip-services3-gox/pip-services3-components-gox/lock"
import 'package:pip_services3_components/pip_services3_components.dart';
from pip_services3_components.lock.NullLock import NullLock
Not available

Lock creation

To create a NullLock, we need to instantiate it. The following line of code demonstrates this:

let lock = new NullLock();
var lockk = new NullLock();
lock := clock.NewNullLock()
var lock = NullLock();
lock = NullLock()
Not available

Lock acquisition and release

The Null lock class does possess the acquireLock() and releaseLock() methods, which can be called in the same manner as they were called for the MemoryLock class. The only difference is that they don’t actually acquire or release any locks, but only simulate these operations.

Example

The following example replaces the MemoryLock used in the previous example with a NullLock. Thus, the locking is only simulated and will not actually prevent simultaneous reading of and/or writing to shared memory.

import { ConfigParams, Descriptor, IReferenceable, IReferences, References } from "pip-services3-commons-nodex";
import { ICache, MemoryCache, MemoryLock } from "pip-services3-components-nodex";

export class MyComponent implements IReferenceable {
    private _cache: ICache;
    private _lock: MemoryLock;

    public setReferences(references: IReferences): void {
        this._cache = references.getOneRequired(new Descriptor("*", "cache", "*", "*", "1.0"));
        this._lock = references.getOneRequired(new Descriptor("*", "lock", "*", "*", "1.0"));
    }
    
    public async storeResult(correlationId: string, param1: string): Promise<void> {
        // Lock
        this._lock.acquireLock(correlationId, "mykey", 1000, 1000);

        let config = ConfigParams.fromTuples("retry_timeout", 200);
        this._lock.configure(config);
        
        // Do processing
        // ...
        console.log("The stored value is " + param1);

        // Store result to cache async
        await  this._cache.store(correlationId, 'mykey', param1, 3600000);
    
        // Release lock async
        await this._lock.releaseLock(correlationId, 'mykey');
    }

    public async obtainResult(correlationId: string): Promise<any> {
        // Lock..
        this._lock.acquireLock(correlationId, "mykey", 1000, 1000);
        
        // Do processing
        // ...
        let result = this._cache.retrieve(correlationId, "mykey");
    
        // Release lock async
        await this._lock.releaseLock(correlationId, "mykey");

        return result
    }
}
    
    
// Use the component
let my_component = new MyComponent();
my_component.setReferences(References.fromTuples(
    new Descriptor("pip-services", "cache", "memory", "default", "1.0"), new MemoryCache(),
    new Descriptor("pip-services", "lock", "null", "default", "1.0"), new NullLock(),
));

await my_component.storeResult(null, "param1");

let result = my_component.obtainResult(null);

console.log("The retrieved value is " + result);
using PipServices3.Commons.Config;
using PipServices3.Components.Lock;
using PipServices3.Components.Cache;
using PipServices3.Commons.Refer;

class MyComponent: IReferenceable
{
    private ICache _cache;
    private MemoryLock _lock;

    public void SetReferences(IReferences references)
    {
        _cache = references.GetOneRequired<ICache>(new Descriptor("*", "cache", "*", "*", "1.0"));
        _lock = references.GetOneRequired<MemoryLock>(new Descriptor("*", "lock", "*", "*", "1.0"));
    }


    public async Task StoreResultAsync(string correlationId, string param1) 
    {
        // Lock
        _lock.AcquireLock(correlationId, "mykey", 1000, 1000);

        var config = ConfigParams.FromTuples("retry_timeout", 200);
        this._lock.Configure(config);

        // Do processing
        // ...
        Console.WriteLine("The stored value is " + param1);

        // Store result to cache async
        await _cache.StoreAsync(correlationId, "mykey", param1, 3600000);

        // Release lock async
        _lock.ReleaseLock(correlationId, "mykey");
    }

    public async Task<string> ObtainResultAsync(string correlationId)
    {
        // Lock..
        this._lock.AcquireLock(correlationId, "mykey", 1000, 1000);

        // Do processing
        // ...
        var result = await this._cache.RetrieveAsync<string>(correlationId, "mykey");

        // Release lock async
        this._lock.ReleaseLock(correlationId, "mykey");

        return result;
    }
}

    
// Use the component
var my_component = new MyComponent();
my_component.SetReferences(References.FromTuples(
    new Descriptor("pip-services", "cache", "memory", "default", "1.0"), new MemoryCache(),
    new Descriptor("pip-services", "lock", "null", "default", "1.0"), new NullLock()
));

await my_component.StoreResultAsync(null, "param1");
var result = my_component.ObtainResultAsync(null);

Console.WriteLine("The retrieved value is " + result);
import (
	"context"
	"fmt"

	cconf "github.com/pip-services3-gox/pip-services3-commons-gox/config"
	crefer "github.com/pip-services3-gox/pip-services3-commons-gox/refer"
	ccache "github.com/pip-services3-gox/pip-services3-components-gox/cache"
	clock "github.com/pip-services3-gox/pip-services3-components-gox/lock"
)

type MyComponent struct {
	cache ccache.ICache[any]
	lock  clock.MemoryLock
}

func (c *MyComponent) SetReferences(ctx context.Context, references crefer.IReferences) {
	res, descrErr := references.GetOneRequired(crefer.NewDescriptor("*", "cache", "*", "*", "1.0"))
	if descrErr != nil {
		panic(descrErr)
	}
	c.cache = res.(ccache.ICache[any])

	res, descrErr = references.GetOneRequired(crefer.NewDescriptor("*", "lock", "*", "*", "1.0"))
	if descrErr != nil {
		panic(descrErr)
	}
	c.lock = res.(clock.MemoryLock)
}

func (c *MyComponent) StoreResult(ctx context.Context, correlationId string, param1 string) {
	// Lock
	c.lock.AcquireLock(ctx, correlationId, "mykey", 1000, 1000)

	config := cconf.NewConfigParamsFromTuples("retry_timeout", 200)
	c.lock.Configure(ctx, config)

	// Do processing
	// ...
	fmt.Println("The stored value is " + param1)

	// Store result to cache async
	c.cache.Store(ctx, correlationId, "mykey", param1, 3600000)

	// Release lock async
	c.lock.ReleaseLock(ctx, correlationId, "mykey")
}

func (c *MyComponent) ObtainResult(ctx context.Context, correlationId string) any {
	// Lock..
	c.lock.AcquireLock(ctx, correlationId, "mykey", 1000, 1000)

	// Do processing
	// ...
	result, err := c.cache.Retrieve(ctx, correlationId, "mykey")

	// Release lock async
	c.lock.ReleaseLock(ctx, correlationId, "mykey")

	return result
}
    
    
func main() {
	ctx := context.Background()
	lock := clock.NewMemoryLock()
	config := cconf.NewConfigParamsFromTuples("retry_timeout", 200)
	lock.Configure(context.Background(), config)

	lock.AcquireLock(context.Background(), "123", "mykey", 1000, 1000)
	lock.ReleaseLock(context.Background(), "123", "mykey")

	// Use the component
	my_component := &MyComponent{}
	my_component.SetReferences(ctx, crefer.NewReferencesFromTuples(ctx,
		crefer.NewDescriptor("pip-services", "cache", "memory", "default", "1.0"), ccache.NewMemoryCache[any](),
		crefer.NewDescriptor("pip-services", "lock", "memory", "default", "1.0"), clock.NewNullLock(),
	))

	my_component.StoreResult(ctx, "123", "param1")

	result := my_component.ObtainResult(ctx, "123")

	fmt.Println("The retrieved value is " + result)
}
import 'package:pip_services3_commons/pip_services3_commons.dart';
import 'package:pip_services3_components/pip_services3_components.dart';

class MyComponent implements IReferenceable {
  late ICache _cache;
  late MemoryLock _lock;

  @override
  void setReferences(IReferences references) {
    _cache =
        references.getOneRequired(Descriptor('*', 'cache', '*', '*', '1.0'));
    _lock = references.getOneRequired(Descriptor('*', 'lock', '*', '*', '1.0'));
  }

  Future<void> storeResult(String? correlationId, String param1) async {
    // Lock
    await _lock.acquireLock(correlationId, 'mykey', 1000, 1000);

    var config = ConfigParams.fromTuples(['retry_timeout', 200]);
    _lock.configure(config);

    // Do processing
    // ...
    print('The stored value is ' + param1);

    // Store result to cache async
    await _cache.store(correlationId, 'mykey', param1, 3600000);

    // Release lock async
    await _lock.releaseLock(correlationId, 'mykey');
  }

  Future<String> obtainResult(String? correlationId) async {
    // Lock..
    await _lock.acquireLock(correlationId, 'mykey', 1000, 1000);

    // Do processing
    // ...
    var result = await _cache.retrieve(correlationId, 'mykey') as String;

    // Release lock async
    await _lock.releaseLock(correlationId, 'mykey');

    return result;
  }
}
    
    
// Use the component
var my_component = MyComponent();
my_component.setReferences(References.fromTuples([
  Descriptor('pip-services', 'cache', 'memory', 'default', '1.0'),
  MemoryCache(),
  Descriptor('pip-services', 'lock', 'null', 'default', '1.0'),
  NullLock()
]));

await my_component.storeResult(null, 'param1');

var result = await my_component.obtainResult(null);

print('The retrieved value is ' + result);
from pip_services3_commons.refer import Descriptor, References, IReferences, IReferenceable
from pip_services3_components.cache import ICache, MemoryCache
from pip_services3_components.lock.ILock import ILock
from pip_services3_components.lock.NullLock import NullLock


class MyComponent(IReferenceable):
    __cache: ICache
    __lock: ILock

    def set_references(self, references: IReferences):
        self.__cache = references.get_one_required(Descriptor("*", "cache", "*", "*", "1.0"))
        self.__lock = references.get_one_required(Descriptor("*", "lock", "*", "*", "1.0"))

    def store_result(self, correlation_id, param1):      

        # Lock
        self.__lock.acquire_lock(correlation_id, "mykey", 1000, 1000, )
        
        # Do processing
        # ...
        print("The stored value is " + param1)

        # Store result to cache async
        self.__cache.store(correlation_id, 'mykey', param1, 3600000)
    
        # Release lock async
        self.__lock.release_lock(correlation_id, 'mykey')

        
    def obtain_result(self, correlation_id):
        
         # Lock..
        self.__lock.acquire_lock(correlation_id, "mykey", 1000, 1000, )
        
        # Do processing
        # ...
        result = self.__cache.retrieve(correlation_id, "mykey")
    
        # Release lock async
        self.__lock.release_lock(correlation_id, "mykey")
    
        return result
    
    
# Use the component
my_component = MyComponent()
my_component.set_references(References.from_tuples(
    Descriptor("pip-services", "cache", "memory", "default", "1.0"), MemoryCache(),
    Descriptor("pip-services", "lock", "null", "default", "1.0"), NullLock(),
))

my_component.store_result(None, "param1")

result = my_component.obtain_result(None)

print("The retrieved value is " + result)
Not available

Which, after running, produces the following outcome:

figure 1

Wrapping up

In this tutorial, we have learned how to use the MemoryLock and NullLock components. First, we examined the ILock interface, which must be implemented by all locks. Next, we learned that the MemoryLock class allows us to create a lock that can be used to synchronize the execution of processes that use shared memory. Then, we saw how to create, acquire and release this type of lock and an example of how to apply these mechanisms inside a component. Finally, we learned that the NullLock is a dummy component that only simulates the behavior of a real lock.