1. Na competição ACM, uma equipe é composta por três jogadores. Agora há N + M alunos, dos quais N alunos são bons em algoritmos, e os alunos M restantes são bons em programação. Esses alunos devem participar da competição ACM. Seu treinador Cada equipe deve ter pelo menos um aluno que seja bom em algoritmos e um aluno que seja bom em programação. Quantas equipes esses alunos podem formar?
Entrada: Insira dois inteiros M e N, onde 1 <N, M <10000000
Saída: o número máximo de equipes que podem ser formadas
#include <iostream>
using namespace std;
int main()
{
int cnt = 0,n,m;
cout << "输入N个擅长算法的,M个擅长编程的:" << endl;
cin >> n >> m;
while(n!=0&&m!=0&&m+n!=2){
if(n>=m){
n = n-2;
m = m-1;
cnt++;
}
else if(n<m){
m = m-2;
n = n-1;
cnt++;
}
}
cout << "最大组对数量" << cnt << endl;
return 0;
}
2. O que é idempotência
O conceito de idempotência: Em termos gerais, idempotência significa que não importa quantas operações repetidas sejam realizadas, o mesmo resultado é alcançado.
3. O que são operações idempotentes em solicitações REST
GET, PUT e DELETE são operações idempotentes, enquanto POST não é uma
análise.
Em primeiro lugar, as solicitações GET são bem compreendidas. Se você consultar recursos várias vezes, os resultados dessa implementação serão os mesmos.
A idempotência da solicitação PUT pode ser entendida desta forma. Modificando A para B, seu primeiro valor de solicitação passa a ser B, e depois realizando esta operação muitas vezes, o resultado final ainda é B, que é o mesmo que o resultado de uma execução, então PUT É uma operação idempotente.
Da mesma forma, a operação DELETE pode ser entendida, após a exclusão do recurso pela primeira vez, a solicitação de exclusão é feita várias vezes posteriormente, e o resultado final é o mesmo, e o recurso é excluído.
POST não é uma operação idempotente, porque uma solicitação adiciona um novo recurso e uma segunda solicitação adiciona dois novos recursos. Várias solicitações produzirão resultados diferentes, portanto, POST não é uma operação idempotente.
4. Distinguir o uso de POST e PUT com base na idempotência
Pode ser distinguido de acordo com idempotente (idempotente).
Para dar um exemplo simples, se houver um sistema de blog que fornece uma API da Web, o modo é como este http: // superblogging / blogs / {blog-name}, muito simples, substitua {blog-name} pelo nome do nosso blog, vá para Este URL envia uma solicitação HTTP PUT ou POST. A parte do corpo do HTTP é a postagem do blog. Este é um exemplo de API REST muito simples.
Devemos usar o método PUT ou o método POST?
Depende se o comportamento desse serviço REST é idempotente, se enviarmos duas solicitações http: // superblogging / blogs / post / Sample, que tipo de comportamento existe no lado do servidor? Se duas postagens de blog forem geradas, significa que esse serviço não é idempotente, porque o uso múltiplo tem efeitos colaterais; se a primeira solicitação for substituída pela última solicitação, o serviço será idempotente. No primeiro caso, o método POST deve ser usado e, no último caso, o método PUT deve ser usado.
5. As deficiências do CAS e suas soluções.
As deficiências do CAS são como problema ABA, problema de consumo de spin lock, problema de consistência de compartilhamento multivariável
1.ABA:
Descrição do problema: Thread t1 muda seu valor de A para B, e então de B para A. Ao mesmo tempo, um segmento t2 deseja alterar o valor de A para C. Mas quando o CAS verifica, ele descobrirá que não há mudança, mas na verdade ele mudou. Pode causar perda de dados.
Solução: CAS ainda é semelhante ao bloqueio otimista, adicionando um número de versão ou carimbo de data / hora a ele da mesma maneira que o bloqueio otimista de dados, como AtomicStampedReference
2. Spin consome recursos:
Descrição do problema: Quando vários threads competem pelo mesmo recurso, se spin Se não obtiver sucesso, a CPU estará sempre ocupada.
Solução: Destrua o loop for infinito. Quando um determinado tempo ou número de vezes for excedido, o retorno será encerrado. O LongAddr adicionado no JDK8 é semelhante ao ConcurrentHashMap. Quando vários encadeamentos competem, a granularidade é reduzida e uma variável é dividida em várias variáveis para obter o efeito de vários encadeamentos acessando vários recursos e, finalmente, sum é chamado para combiná-los.
Embora a base e as células sejam modificadas por volatilidade, parece que a operação de soma não está bloqueada e o resultado da soma pode não ser tão preciso.
2. Problema de consistência de compartilhamento multivariável:
Solução: a operação CAS é para uma variável, se você operar em várias variáveis,
- Isso pode ser resolvido com um bloqueio.
- Empacotado em classes de objetos para resolver.
(Mais C / C ++ gratuito, Linux, Nginx, ZeroMQ, MySQL, Redis, fastdfs, MongoDB, ZK, streaming media, CDN, P2P, K8S, Docker, TCP / IP, corrotina, DPDK, etc. vários pontos de conhecimento produtos secos Materiais de aprendizagem mais grupo 960994558)
6. Recursos da árvore B +
(1) O número de palavras-chave de cada nó é igual ao número de filhos. As palavras-chave de todos os nós internos não inferiores são as maiores na subárvore correspondente, e os nós internos inferiores contêm todas as palavras-chave .
(2) Exceto para o nó raiz, cada nó interno tem até m filhos. [3]
(3) Todos os nós folha estão no mesmo nível da estrutura da árvore e não contêm nenhuma informação (podem ser considerados nós externos ou nós que falham na pesquisa). Portanto, a estrutura da árvore está sempre equilibrada na altura da árvore.
7. Explique em detalhes o isolamento das transações.
Isolamento de
transações Para garantir o isolamento de transações, podemos naturalmente projetar transações para serem single-threaded. Desta forma, a eficiência será extremamente baixa. Para garantir o isolamento sem perder eficiência, dividimos a perda de isolamento em três situações .
Leitura suja: lê os dados de outra transação não confirmada.
Leitura fantasma: A tabela foi lida uma vez durante uma transação. Neste momento, outra confirmação de transação acontece para fazer com que a transação leia a tabela novamente de forma inconsistente. (Impacto da tabela)
Leitura não repetível: Os dados de a foram lidos uma vez durante uma transação e outra transação é confirmada neste momento, o que faz com que a transação leia os dados de novamente de forma inconsistente.
Quatro níveis de isolamento são introduzidos para essas três situações.
Quatro níveis de isolamento:
leitura não confirmada - não evita problemas de isolamento, com problemas de leitura suja / não repetibilidade / leitura fantasma (leitura fantasma)
Leitura confirmada - pode evitar problemas de leitura suja, Mas não pode evitar problemas de não repetibilidade / leitura fantasma (leitura fantasma) Leitura
repetível - pode evitar problemas de leitura suja / leitura não repetível, mas não pode prevenir problemas de leitura virtual (leitura fantasma)
Serializável - o banco de dados é projetado como um banco de dados de thread único, o que pode evitar o acima Todos os problemas
Esses quatro níveis de isolamento aumentam a segurança. Diminuindo a eficiência
8. Sabendo que uma função rand7 () pode gerar números aleatórios de 1-7, forneça uma função que possa gerar números aleatórios de 1-10. A solução é baseada em um método chamado amostragem de rejeição. A ideia principal é que, enquanto um número aleatório dentro do intervalo de destino for gerado, ele será retornado diretamente. Se o número aleatório gerado não estiver dentro do intervalo de destino, descarte o valor e faça uma nova amostra. Uma vez que os números no intervalo de destino são selecionados com probabilidade igual, tal distribuição uniforme é gerada.
Obviamente, rand7 precisa ser executado pelo menos duas vezes, caso contrário, o número 1-10 não será gerado. Executando rand7 duas vezes, inteiros de 1-49 podem ser gerados,
Uma vez que 49 não é um múltiplo de 10, precisamos descartar alguns valores. O intervalo de números que queremos é de 1 a 40. Se não estiver neste intervalo, descarte e faça uma nova amostra.
Código:
int rand10() {
int row, col, idx;
do {
row = rand7();
col = rand7();
idx = col + (row-1)*7;
} while (idx > 40);
return 1 + (idx-1)%10;
}
Como o intervalo de linhas é 1-7 e o intervalo col é 1-7, o intervalo de valores idx é 1-49. Valores maiores que 40 são descartados, de forma que os números restantes no intervalo de 1 a 40 sejam retornados por módulo. Vamos calcular o valor esperado do número de vezes de amostragem que atende ao intervalo de 1-40:
9. Explique quais medidas de otimização estão disponíveis para o pool de threads.
Quanto maior for a proporção do tempo de espera do thread, mais threads serão necessários. Quanto maior a proporção de tempo de CPU do thread, menos threads são necessários.
Se você usa uma operação intensiva de CPU, o número de threads é igual ao número de núcleos de CPU, evitando muitos contextos de thread de comutação inúteis.
Se você usa muito IO e precisa de muita espera, o número de threads pode ser definido mais, por exemplo Multiplique os núcleos da CPU por 2.
10. C ++ 11 três maneiras de criar threads
- Através do
thread de função :
junção de classe de biblioteca padrão : bloquear o thread principal e esperar
// MultiThread.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#include<iostream>
#include<vector>
#include<map>
#include<string>
#include<thread>
using namespace std;
void myPrint()
{
cout << "线程开始运行" << endl;
cout << "线程运行结束了" << endl;
}
int main()
{
std::thread my2Obj(myPrint); // 可调用对象
my2Obj.join();// 主线程阻塞在这,并等待myPrint()执行完
cout << "wangtao" << endl;
return 0;
}
detach (): separe completamente o thread principal e o thread filho, o thread filho residirá em segundo plano e será executado e será assumido pela biblioteca de tempo de execução C ++ e perderá o controle
void myPrint()
{
cout << "线程开始运行1" << endl;
cout << "线程开始运行2" << endl;
cout << "线程开始运行3" << endl;
cout << "线程开始运行4" << endl;
cout << "线程开始运行5" << endl;
cout << "线程开始运行6" << endl;
cout << "线程开始运行7" << endl;
cout << "线程开始运行8" << endl;
cout << "线程开始运行9" << endl;
}
int main()
{
std::thread my2Obj(myPrint); // 主线程阻塞在这,并等待myPrint()执行完
my2Obj.detach();
cout << "wangtao1" << endl;
cout << "wangtao2" << endl;
cout << "wangtao3" << endl;
cout << "wangtao4" << endl;
cout << "wangtao5" << endl;
cout << "wangtao6" << endl;
cout << "wangtao7" << endl;
cout << "wangtao8" << endl;
return 0;
}
joinable (): determine se você pode usar join () ou detach () com sucesso
Descrição do programa: Join não pode ser implementado após desanexar
int main()
{
std::thread my2Obj(myPrint); // 主线程阻塞在这,并等待myPrint()执行完
if (my2Obj.joinable()){
cout << "1:joinable() == true" << endl;
}
else {
cout << "1:joinable() == false" << endl;
}
my2Obj.detach();
if (my2Obj.joinable()) {
cout << "2:joinable() == true" << endl;
}
else {
cout << "2:joinable() == false" << endl;
}
cout << "wangtao1" << endl;
cout << "wangtao2" << endl;
cout << "wangtao3" << endl;
cout << "wangtao4" << endl;
cout << "wangtao5" << endl;
cout << "wangtao6" << endl;
cout << "wangtao7" << endl;
cout << "wangtao8" << endl;
return 0;
}
int main()
{
std::thread my2Obj(myPrint); // 主线程阻塞在这,并等待myPrint()执行完
if (my2Obj.joinable()){
my2Obj.join();
}
cout << "wangtao1" << endl;
cout << "wangtao2" << endl;
cout << "wangtao3" << endl;
cout << "wangtao4" << endl;
cout << "wangtao5" << endl;
cout << "wangtao6" << endl;
cout << "wangtao7" << endl;
cout << "wangtao8" << endl;
return 0;
}
2. Criar thread por meio de objeto de classe
class CObject
{
public:
void operator ()() {
cout << "线程开始运行" << endl;
cout << "线程结束运行" << endl;
}
};
int main()
{
CObject obj;
std::thread my2Obj(obj); // 主线程阻塞在这,并等待myPrint()执行完
if (my2Obj.joinable()){
my2Obj.join();
}
cout << "see you " << endl;
return 0;
}
class CObject
{
int& m_obj;
public:
CObject(int& i) :m_obj(i) {
}
void operator ()() {
// 不带参数
cout << "线程开始运行1" << endl;
cout << "线程开始运行2" << endl;
cout << "线程开始运行3" << endl;
cout << "线程开始运行4" << endl;
cout << "线程开始运行5" << endl;
}
};
int main()
{
int i = 6;
CObject obj(i);
std::thread my2Obj(obj); // 主线程阻塞在这,并等待myPrint()执行完
if (my2Obj.joinable()){
my2Obj.detach();
}
cout << "see you " << endl;
return 0;
}
O objeto é destruído quando o thread principal termina com detach (). As funções de membro dos threads filhos podem ser chamadas?
O objeto aqui será copiado para o thread filho. Quando o thread principal terminar, o objeto do thread filho copiado não será destruído.
Enquanto não houver referência, o ponteiro não causará problemas.
Verifique se o objeto é copiado para o thread filho, copiando o construtor e o destruidor
// MultiThread.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include<iostream>
#include<vector>
#include<map>
#include<string>
#include<thread>
using namespace std;
class CObject
{
int& m_obj;
public:
CObject(int& i) :m_obj(i) {
cout << "ctor" << endl;
}
CObject(const CObject& m) :m_obj(m.m_obj) {
cout << "copy ctor" << endl;
}
~CObject(){
cout << "dtor" << endl;
}
void operator ()() {
// 不带参数
cout << "线程开始运行1" << endl;
cout << "线程开始运行2" << endl;
cout << "线程开始运行3" << endl;
cout << "线程开始运行4" << endl;
cout << "线程开始运行5" << endl;
}
};
int main()
{
int i = 6;
CObject obj(i);
std::thread my2Obj(obj); // 主线程阻塞在这,并等待myPrint()执行完
if (my2Obj.joinable()){
my2Obj.detach();
}
cout << "see you " << endl;
return 0;
}
O destruidor do thread filho é executado em segundo plano, portanto, o dtor de saída é o thread principal. O resultado do uso de join () é:
3. Crie threads por meio de expressões lambda
int main()
{
auto myLamThread = [] {
cout << "线程开始运行" << endl;
cout << "线程结束运行" << endl;
};
thread cthread(myLamThread);
cthread.join();
std::cout << "see you " << endl;
return 0;
}
É melhor aplicar o que você aprendeu, e pode haver deficiências no acima, bem-vindo para apontar a discussão.